/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.commons.compress.archivers.zip; /** * Strong Encryption Header (0x0017). * * <p>Certificate-based encryption:</p> * * <pre> * Value Size Description * ----- ---- ----------- * 0x0017 2 bytes Tag for this "extra" block type * TSize 2 bytes Size of data that follows * Format 2 bytes Format definition for this record * AlgID 2 bytes Encryption algorithm identifier * Bitlen 2 bytes Bit length of encryption key (32-448 bits) * Flags 2 bytes Processing flags * RCount 4 bytes Number of recipients. * HashAlg 2 bytes Hash algorithm identifier * HSize 2 bytes Hash size * SRList (var) Simple list of recipients hashed public keys * * Flags - This defines the processing flags. * </pre> * * <ul> * <li>0x0007 - reserved for future use * <li>0x000F - reserved for future use * <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 wrapping is not * used when generating a Master Session Key using * ErdData. * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the * same algorithm used for encrypting the file contents. * <li>0x8000 - reserved for future use * </ul> * * <pre> * RCount - This defines the number intended recipients whose * public keys were used for encryption. This identifies * the number of elements in the SRList. * * see also: reserved1 * * HashAlg - This defines the hash algorithm used to calculate * the public key hash of each public key used * for encryption. This field currently supports * only the following value for SHA-1 * * 0x8004 - SHA1 * * HSize - This defines the size of a hashed public key. * * SRList - This is a variable length list of the hashed * public keys for each intended recipient. Each * element in this list is HSize. The total size of * SRList is determined using RCount * HSize. * </pre> * * <p>Password-based Extra Field 0x0017 in central header only.</p> * * <pre> * Value Size Description * ----- ---- ----------- * 0x0017 2 bytes Tag for this "extra" block type * TSize 2 bytes Size of data that follows * Format 2 bytes Format definition for this record * AlgID 2 bytes Encryption algorithm identifier * Bitlen 2 bytes Bit length of encryption key (32-448 bits) * Flags 2 bytes Processing flags * (more?) * </pre> * * <p><b>Format</b> - the data format identifier for this record. The only value * allowed at this time is the integer value 2.</p> * * <p>Password-based Extra Field 0x0017 preceding compressed file data.</p> * * <pre> * Value Size Description * ----- ---- ----------- * 0x0017 2 bytes Tag for this "extra" block type * IVSize 2 bytes Size of initialization vector (IV) * IVData IVSize Initialization vector for this file * Size 4 bytes Size of remaining decryption header data * Format 2 bytes Format definition for this record * AlgID 2 bytes Encryption algorithm identifier * Bitlen 2 bytes Bit length of encryption key (32-448 bits) * Flags 2 bytes Processing flags * ErdSize 2 bytes Size of Encrypted Random Data * ErdData ErdSize Encrypted Random Data * Reserved1 4 bytes Reserved certificate processing data * Reserved2 (var) Reserved for certificate processing data * VSize 2 bytes Size of password validation data * VData VSize-4 Password validation data * VCRC32 4 bytes Standard ZIP CRC32 of password validation data * * IVData - The size of the IV should match the algorithm block size. * The IVData can be completely random data. If the size of * the randomly generated data does not match the block size * it should be complemented with zero's or truncated as * necessary. If IVSize is 0,then IV = CRC32 + Uncompressed * File Size (as a 64 bit little-endian, unsigned integer value). * * Format - the data format identifier for this record. The only * value allowed at this time is the integer value 2. * * ErdData - Encrypted random data is used to store random data that * is used to generate a file session key for encrypting * each file. SHA1 is used to calculate hash data used to * derive keys. File session keys are derived from a master * session key generated from the user-supplied password. * If the Flags field in the decryption header contains * the value 0x4000, then the ErdData field must be * decrypted using 3DES. If the value 0x4000 is not set, * then the ErdData field must be decrypted using AlgId. * * Reserved1 - Reserved for certificate processing, if value is * zero, then Reserved2 data is absent. See the explanation * under the Certificate Processing Method for details on * this data structure. * * Reserved2 - If present, the size of the Reserved2 data structure * is located by skipping the first 4 bytes of this field * and using the next 2 bytes as the remaining size. See * the explanation under the Certificate Processing Method * for details on this data structure. * * VSize - This size value will always include the 4 bytes of the * VCRC32 data and will be greater than 4 bytes. * * VData - Random data for password validation. This data is VSize * in length and VSize must be a multiple of the encryption * block size. VCRC32 is a checksum value of VData. * VData and VCRC32 are stored encrypted and start the * stream of encrypted data for a file. * </pre> * * <p>Reserved1 - Certificate Decryption Header Reserved1 Data:</p> * * <pre> * Value Size Description * ----- ---- ----------- * RCount 4 bytes Number of recipients. * </pre> * * <p>RCount - This defines the number intended recipients whose public keys were * used for encryption. This defines the number of elements in the REList field * defined below.</p> * * <p>Reserved2 - Certificate Decryption Header Reserved2 Data Structures:</p> * * <pre> * Value Size Description * ----- ---- ----------- * HashAlg 2 bytes Hash algorithm identifier * HSize 2 bytes Hash size * REList (var) List of recipient data elements * * HashAlg - This defines the hash algorithm used to calculate * the public key hash of each public key used * for encryption. This field currently supports * only the following value for SHA-1 * * 0x8004 - SHA1 * * HSize - This defines the size of a hashed public key * defined in REHData. * * REList - This is a variable length of list of recipient data. * Each element in this list consists of a Recipient * Element data structure as follows: * </pre> * * <p>Recipient Element (REList) Data Structure:</p> * * <pre> * Value Size Description * ----- ---- ----------- * RESize 2 bytes Size of REHData + REKData * REHData HSize Hash of recipients public key * REKData (var) Simple key blob * * * RESize - This defines the size of an individual REList * element. This value is the combined size of the * REHData field + REKData field. REHData is defined by * HSize. REKData is variable and can be calculated * for each REList element using RESize and HSize. * * REHData - Hashed public key for this recipient. * * REKData - Simple Key Blob. The format of this data structure * is identical to that defined in the Microsoft * CryptoAPI and generated using the CryptExportKey() * function. The version of the Simple Key Blob * supported at this time is 0x02 as defined by * Microsoft. * * For more details see https://msdn.microsoft.com/en-us/library/aa920051.aspx * </pre> * * <p><b>Flags</b> - Processing flags needed for decryption</p> * * <ul> * <li>0x0001 - Password is required to decrypt</li> * <li>0x0002 - Certificates only</li> * <li>0x0003 - Password or certificate required to decrypt</li> * <li>0x0007 - reserved for future use * <li>0x000F - reserved for future use * <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 is not used when generating a Master Session Key using ErdData. * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same * algorithm used for encrypting the file contents. * <li>0x8000 - reserved for future use. * </ul> * * <p><b>See the section describing the Strong Encryption Specification for * details. Refer to the section in this document entitled * "Incorporating PKWARE Proprietary Technology into Your Product" for more * information.</b></p> * * @NotThreadSafe * @since 1.11 */ public class X0017_StrongEncryptionHeader extends PKWareExtraHeader { public X0017_StrongEncryptionHeader() { super(new ZipShort(0x0017)); } private int format; // TODO written but not read private EncryptionAlgorithm algId; private int bitlen; // TODO written but not read private int flags; // TODO written but not read private long rcount; private HashAlgorithm hashAlg; private int hashSize; // encryption data private byte ivData[]; private byte erdData[]; // encryption key private byte recipientKeyHash[]; private byte keyBlob[]; // password verification data private byte vData[]; private byte vCRC32[]; /** * Get record count. * @return the record count */ public long getRecordCount() { return rcount; } /** * Get hash algorithm. * @return the hash algorithm */ public HashAlgorithm getHashAlgorithm() { return hashAlg; } /** * Get encryption algorithm. * @return the encryption algorithm */ public EncryptionAlgorithm getEncryptionAlgorithm() { return algId; } /** * Parse central directory format. * * @param data the buffer to read data from * @param offset offset into buffer to read data * @param length the length of data */ public void parseCentralDirectoryFormat(final byte[] data, final int offset, final int length) { this.format = ZipShort.getValue(data, offset); this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2)); this.bitlen = ZipShort.getValue(data, offset + 4); this.flags = ZipShort.getValue(data, offset + 6); this.rcount = ZipLong.getValue(data, offset + 8); if (rcount > 0) { this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 12)); this.hashSize = ZipShort.getValue(data, offset + 14); // srlist... hashed public keys for (int i = 0; i < this.rcount; i++) { for (int j = 0; j < this.hashSize; j++) { // ZipUtil.signedByteToUnsignedInt(data[offset + 16 + (i * this.hashSize) + j])); } } } } /** * Parse file header format. * * <p>(Password only?)</p> * * @param data the buffer to read data from * @param offset offset into buffer to read data * @param length the length of data */ public void parseFileFormat(final byte[] data, final int offset, final int length) { final int ivSize = ZipShort.getValue(data, offset); this.ivData = new byte[ivSize]; System.arraycopy(data, offset + 4, this.ivData, 0, ivSize); this.format = ZipShort.getValue(data, offset + ivSize + 6); this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 8)); this.bitlen = ZipShort.getValue(data, offset + ivSize + 10); this.flags = ZipShort.getValue(data, offset + ivSize + 12); final int erdSize = ZipShort.getValue(data, offset + ivSize + 14); this.erdData = new byte[erdSize]; System.arraycopy(data, offset + ivSize + 16, this.erdData, 0, erdSize); this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize); System.out.println("rcount: " + rcount); if (rcount == 0) { final int vSize = ZipShort.getValue(data, offset + ivSize + 20 + erdSize); this.vData = new byte[vSize - 4]; this.vCRC32 = new byte[4]; System.arraycopy(data, offset + ivSize + 22 + erdSize , this.vData, 0, vSize - 4); System.arraycopy(data, offset + ivSize + 22 + erdSize + vSize - 4, vCRC32, 0, 4); } else { this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + erdSize)); this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + erdSize); final int resize = ZipShort.getValue(data, offset + ivSize + 24 + erdSize); this.recipientKeyHash = new byte[this.hashSize]; this.keyBlob = new byte[resize - this.hashSize]; System.arraycopy(data, offset + ivSize + 24 + erdSize, this.recipientKeyHash, 0, this.hashSize); System.arraycopy(data, offset + ivSize + 24 + erdSize + this.hashSize, this.keyBlob, 0, resize - this.hashSize); final int vSize = ZipShort.getValue(data, offset + ivSize + 26 + erdSize + resize); this.vData = new byte[vSize - 4]; this.vCRC32 = new byte[4]; System.arraycopy(data, offset + ivSize + 22 + erdSize + resize, this.vData, 0, vSize - 4); System.arraycopy(data, offset + ivSize + 22 + erdSize + resize + vSize - 4, vCRC32, 0, 4); } // validate values? } @Override public void parseFromLocalFileData(final byte[] data, final int offset, final int length) { super.parseFromLocalFileData(data, offset, length); parseFileFormat(data, offset, length); } @Override public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) { super.parseFromCentralDirectoryData(data, offset, length); parseCentralDirectoryFormat(data, offset, length); } }