X0017_StrongEncryptionHeader.java

  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. import java.util.Arrays;
  21. import java.util.zip.ZipException;

  22. /**
  23.  * Strong Encryption Header (0x0017).
  24.  *
  25.  * <p>
  26.  * Certificate-based encryption:
  27.  * </p>
  28.  *
  29.  * <pre>
  30.  * Value     Size     Description
  31.  * -----     ----     -----------
  32.  * 0x0017    2 bytes  Tag for this "extra" block type
  33.  * TSize     2 bytes  Size of data that follows
  34.  * Format    2 bytes  Format definition for this record
  35.  * AlgID     2 bytes  Encryption algorithm identifier
  36.  * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
  37.  * Flags     2 bytes  Processing flags
  38.  * RCount    4 bytes  Number of recipients.
  39.  * HashAlg   2 bytes  Hash algorithm identifier
  40.  * HSize     2 bytes  Hash size
  41.  * SRList    (var)    Simple list of recipients hashed public keys
  42.  *
  43.  * Flags -   This defines the processing flags.
  44.  * </pre>
  45.  *
  46.  * <ul>
  47.  * <li>0x0007 - reserved for future use
  48.  * <li>0x000F - reserved for future use
  49.  * <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
  50.  * wrapping is not used when generating a Master Session Key using ErdData.
  51.  * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same algorithm used for encrypting the file contents.
  52.  * <li>0x8000 - reserved for future use
  53.  * </ul>
  54.  *
  55.  * <pre>
  56.  * RCount - This defines the number intended recipients whose
  57.  *          public keys were used for encryption.  This identifies
  58.  *          the number of elements in the SRList.
  59.  *
  60.  *          see also: reserved1
  61.  *
  62.  * HashAlg - This defines the hash algorithm used to calculate
  63.  *           the public key hash of each public key used
  64.  *           for encryption. This field currently supports
  65.  *           only the following value for SHA-1
  66.  *
  67.  *           0x8004 - SHA1
  68.  *
  69.  * HSize -   This defines the size of a hashed public key.
  70.  *
  71.  * SRList -  This is a variable length list of the hashed
  72.  *           public keys for each intended recipient.  Each
  73.  *           element in this list is HSize.  The total size of
  74.  *           SRList is determined using RCount * HSize.
  75.  * </pre>
  76.  *
  77.  * <p>
  78.  * Password-based Extra Field 0x0017 in central header only.
  79.  * </p>
  80.  *
  81.  * <pre>
  82.  * Value     Size     Description
  83.  * -----     ----     -----------
  84.  * 0x0017    2 bytes  Tag for this "extra" block type
  85.  * TSize     2 bytes  Size of data that follows
  86.  * Format    2 bytes  Format definition for this record
  87.  * AlgID     2 bytes  Encryption algorithm identifier
  88.  * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
  89.  * Flags     2 bytes  Processing flags
  90.  * (more?)
  91.  * </pre>
  92.  *
  93.  * <p>
  94.  * <b>Format</b> - the data format identifier for this record. The only value allowed at this time is the integer value 2.
  95.  * </p>
  96.  *
  97.  * <p>
  98.  * Password-based Extra Field 0x0017 preceding compressed file data.
  99.  * </p>
  100.  *
  101.  * <pre>
  102.  * Value     Size     Description
  103.  * -----     ----     -----------
  104.  * 0x0017    2 bytes  Tag for this "extra" block type
  105.  * IVSize    2 bytes  Size of initialization vector (IV)
  106.  * IVData    IVSize   Initialization vector for this file
  107.  * Size      4 bytes  Size of remaining decryption header data
  108.  * Format    2 bytes  Format definition for this record
  109.  * AlgID     2 bytes  Encryption algorithm identifier
  110.  * Bitlen    2 bytes  Bit length of encryption key (32-448 bits)
  111.  * Flags     2 bytes  Processing flags
  112.  * ErdSize   2 bytes  Size of Encrypted Random Data
  113.  * ErdData   ErdSize  Encrypted Random Data
  114.  * Reserved1 4 bytes  Reserved certificate processing data
  115.  * Reserved2 (var)    Reserved for certificate processing data
  116.  * VSize     2 bytes  Size of password validation data
  117.  * VData     VSize-4  Password validation data
  118.  * VCRC32    4 bytes  Standard ZIP CRC32 of password validation data
  119.  *
  120.  * IVData - The size of the IV should match the algorithm block size.
  121.  *          The IVData can be completely random data.  If the size of
  122.  *          the randomly generated data does not match the block size
  123.  *          it should be complemented with zero's or truncated as
  124.  *          necessary.  If IVSize is 0, then IV = CRC32 + Uncompressed
  125.  *          File Size (as a 64 bit little-endian, unsigned integer value).
  126.  *
  127.  * Format -  the data format identifier for this record.  The only
  128.  *           value allowed at this time is the integer value 2.
  129.  *
  130.  * ErdData - Encrypted random data is used to store random data that
  131.  *           is used to generate a file session key for encrypting
  132.  *           each file.  SHA1 is used to calculate hash data used to
  133.  *           derive keys.  File session keys are derived from a master
  134.  *           session key generated from the user-supplied password.
  135.  *           If the Flags field in the decryption header contains
  136.  *           the value 0x4000, then the ErdData field must be
  137.  *           decrypted using 3DES. If the value 0x4000 is not set,
  138.  *           then the ErdData field must be decrypted using AlgId.
  139.  *
  140.  * Reserved1 - Reserved for certificate processing, if value is
  141.  *           zero, then Reserved2 data is absent.  See the explanation
  142.  *           under the Certificate Processing Method for details on
  143.  *           this data structure.
  144.  *
  145.  * Reserved2 - If present, the size of the Reserved2 data structure
  146.  *           is located by skipping the first 4 bytes of this field
  147.  *           and using the next 2 bytes as the remaining size.  See
  148.  *           the explanation under the Certificate Processing Method
  149.  *           for details on this data structure.
  150.  *
  151.  * VSize - This size value will always include the 4 bytes of the
  152.  *         VCRC32 data and will be greater than 4 bytes.
  153.  *
  154.  * VData - Random data for password validation.  This data is VSize
  155.  *         in length and VSize must be a multiple of the encryption
  156.  *         block size.  VCRC32 is a checksum value of VData.
  157.  *         VData and VCRC32 are stored encrypted and start the
  158.  *         stream of encrypted data for a file.
  159.  * </pre>
  160.  *
  161.  * <p>
  162.  * Reserved1 - Certificate Decryption Header Reserved1 Data:
  163.  * </p>
  164.  *
  165.  * <pre>
  166.  * Value     Size     Description
  167.  * -----     ----     -----------
  168.  * RCount    4 bytes  Number of recipients.
  169.  * </pre>
  170.  *
  171.  * <p>
  172.  * RCount - This defines the number intended recipients whose public keys were used for encryption. This defines the number of elements in the REList field
  173.  * defined below.
  174.  * </p>
  175.  *
  176.  * <p>
  177.  * Reserved2 - Certificate Decryption Header Reserved2 Data Structures:
  178.  * </p>
  179.  *
  180.  * <pre>
  181.  * Value     Size     Description
  182.  * -----     ----     -----------
  183.  * HashAlg   2 bytes  Hash algorithm identifier
  184.  * HSize     2 bytes  Hash size
  185.  * REList    (var)    List of recipient data elements
  186.  *
  187.  * HashAlg - This defines the hash algorithm used to calculate
  188.  *           the public key hash of each public key used
  189.  *           for encryption. This field currently supports
  190.  *           only the following value for SHA-1
  191.  *
  192.  *               0x8004 - SHA1
  193.  *
  194.  * HSize -   This defines the size of a hashed public key
  195.  *           defined in REHData.
  196.  *
  197.  * REList -  This is a variable length of list of recipient data.
  198.  *           Each element in this list consists of a Recipient
  199.  *           Element data structure as follows:
  200.  * </pre>
  201.  *
  202.  * <p>
  203.  * Recipient Element (REList) Data Structure:
  204.  * </p>
  205.  *
  206.  * <pre>
  207.  * Value     Size     Description
  208.  * -----     ----     -----------
  209.  * RESize    2 bytes  Size of REHData + REKData
  210.  * REHData   HSize    Hash of recipients public key
  211.  * REKData   (var)    Simple key blob
  212.  *
  213.  *
  214.  * RESize -  This defines the size of an individual REList
  215.  *           element.  This value is the combined size of the
  216.  *           REHData field + REKData field.  REHData is defined by
  217.  *           HSize.  REKData is variable and can be calculated
  218.  *           for each REList element using RESize and HSize.
  219.  *
  220.  * REHData - Hashed public key for this recipient.
  221.  *
  222.  * REKData - Simple Key Blob.  The format of this data structure
  223.  *           is identical to that defined in the Microsoft
  224.  *           CryptoAPI and generated using the CryptExportKey()
  225.  *           function.  The version of the Simple Key Blob
  226.  *           supported at this time is 0x02 as defined by
  227.  *           Microsoft.
  228.  *
  229.  *           For more details see https://msdn.microsoft.com/en-us/library/aa920051.aspx
  230.  * </pre>
  231.  *
  232.  * <p>
  233.  * <b>Flags</b> - Processing flags needed for decryption
  234.  * </p>
  235.  *
  236.  * <ul>
  237.  * <li>0x0001 - Password is required to decrypt</li>
  238.  * <li>0x0002 - Certificates only</li>
  239.  * <li>0x0003 - Password or certificate required to decrypt</li>
  240.  * <li>0x0007 - reserved for future use
  241.  * <li>0x000F - reserved for future use
  242.  * <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
  243.  * is not used when generating a Master Session Key using ErdData.
  244.  * <li>0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the same algorithm used for encrypting the file contents.
  245.  * <li>0x8000 - reserved for future use.
  246.  * </ul>
  247.  *
  248.  * <p>
  249.  * <b>See the section describing the Strong Encryption Specification for details. Refer to the section in this document entitled "Incorporating PKWARE
  250.  * Proprietary Technology into Your Product" for more information.</b>
  251.  * </p>
  252.  *
  253.  * @NotThreadSafe
  254.  * @since 1.11
  255.  */
  256. public class X0017_StrongEncryptionHeader extends PKWareExtraHeader {

  257.     static final ZipShort HEADER_ID = new ZipShort(0x0017);

  258.     private int format; // TODO written but not read

  259.     private EncryptionAlgorithm algId;
  260.     private int bitlen; // TODO written but not read
  261.     private int flags; // TODO written but not read
  262.     private long rcount;
  263.     private HashAlgorithm hashAlg;
  264.     private int hashSize;

  265.     /** Encryption data/ */
  266.     private byte[] ivData;

  267.     private byte[] erdData;

  268.     /** Encryption key. */
  269.     private byte[] recipientKeyHash;

  270.     private byte[] keyBlob;

  271.     /** Password verification data. */
  272.     private byte[] vData;

  273.     private byte[] vCRC32;

  274.     public X0017_StrongEncryptionHeader() {
  275.         super(HEADER_ID);
  276.     }

  277.     private void assertDynamicLengthFits(final String what, final int dynamicLength, final int prefixLength, final int length) throws ZipException {
  278.         if (prefixLength + dynamicLength > length) {
  279.             throw new ZipException("Invalid X0017_StrongEncryptionHeader: " + what + " " + dynamicLength + " doesn't fit into " + length
  280.                     + " bytes of data at position " + prefixLength);
  281.         }
  282.     }

  283.     /**
  284.      * Gets encryption algorithm.
  285.      *
  286.      * @return the encryption algorithm
  287.      */
  288.     public EncryptionAlgorithm getEncryptionAlgorithm() {
  289.         return algId;
  290.     }

  291.     /**
  292.      * Gets hash algorithm.
  293.      *
  294.      * @return the hash algorithm
  295.      */
  296.     public HashAlgorithm getHashAlgorithm() {
  297.         return hashAlg;
  298.     }

  299.     /**
  300.      * Gets record count.
  301.      *
  302.      * @return the record count
  303.      */
  304.     public long getRecordCount() {
  305.         return rcount;
  306.     }

  307.     /**
  308.      * Parse central directory format.
  309.      *
  310.      * @param data   the buffer to read data from
  311.      * @param offset offset into buffer to read data
  312.      * @param length the length of data
  313.      * @throws ZipException if an error occurs
  314.      */
  315.     public void parseCentralDirectoryFormat(final byte[] data, final int offset, final int length) throws ZipException {
  316.         assertMinimalLength(12, length);
  317.         // TODO: double check we really do not want to call super here
  318.         this.format = ZipShort.getValue(data, offset);
  319.         this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 2));
  320.         this.bitlen = ZipShort.getValue(data, offset + 4);
  321.         this.flags = ZipShort.getValue(data, offset + 6);
  322.         this.rcount = ZipLong.getValue(data, offset + 8);

  323.         if (rcount > 0) {
  324.             assertMinimalLength(16, length);
  325.             this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + 12));
  326.             this.hashSize = ZipShort.getValue(data, offset + 14);
  327.         }
  328.     }

  329.     /**
  330.      * Parse file header format.
  331.      *
  332.      * <p>
  333.      * (Password only?)
  334.      * </p>
  335.      *
  336.      * @param data   the buffer to read data from
  337.      * @param offset offset into buffer to read data
  338.      * @param length the length of data
  339.      * @throws ZipException if an error occurs
  340.      */
  341.     public void parseFileFormat(final byte[] data, final int offset, final int length) throws ZipException {
  342.         assertMinimalLength(4, length);
  343.         final int ivSize = ZipShort.getValue(data, offset);
  344.         assertDynamicLengthFits("ivSize", ivSize, 4, length);
  345.         assertMinimalLength(offset + 4, ivSize);
  346.         // TODO: what is at offset + 2?
  347.         this.ivData = Arrays.copyOfRange(data, offset + 4, ivSize);

  348.         assertMinimalLength(16 + ivSize, length); // up to and including erdSize
  349.         // TODO: what is at offset + 4 + ivSize?
  350.         this.format = ZipShort.getValue(data, offset + ivSize + 6);
  351.         this.algId = EncryptionAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 8));
  352.         this.bitlen = ZipShort.getValue(data, offset + ivSize + 10);
  353.         this.flags = ZipShort.getValue(data, offset + ivSize + 12);

  354.         final int erdSize = ZipShort.getValue(data, offset + ivSize + 14);
  355.         assertDynamicLengthFits("erdSize", erdSize, ivSize + 16, length);
  356.         assertMinimalLength(offset + ivSize + 16, erdSize);
  357.         this.erdData = Arrays.copyOfRange(data, offset + ivSize + 16, erdSize);

  358.         assertMinimalLength(16 + 4 + ivSize + erdSize, length);
  359.         this.rcount = ZipLong.getValue(data, offset + ivSize + 16 + erdSize);
  360.         if (rcount == 0) {
  361.             assertMinimalLength(ivSize + 20 + erdSize + 2, length);
  362.             final int vSize = ZipShort.getValue(data, offset + ivSize + 20 + erdSize);
  363.             assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize, length);
  364.             if (vSize < 4) {
  365.                 throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
  366.             }
  367.             assertMinimalLength(offset + ivSize + 22 + erdSize, vSize - 4);
  368.             this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize, vSize - 4);
  369.             assertMinimalLength(offset + ivSize + 22 + erdSize + vSize - 4, 4);
  370.             this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + vSize - 4, 4);
  371.         } else {
  372.             assertMinimalLength(ivSize + 20 + erdSize + 6, length); // up to and including resize
  373.             this.hashAlg = HashAlgorithm.getAlgorithmByCode(ZipShort.getValue(data, offset + ivSize + 20 + erdSize));
  374.             this.hashSize = ZipShort.getValue(data, offset + ivSize + 22 + erdSize);
  375.             final int resize = ZipShort.getValue(data, offset + ivSize + 24 + erdSize);

  376.             if (resize < this.hashSize) {
  377.                 throw new ZipException("Invalid X0017_StrongEncryptionHeader: resize " + resize + " is too small to hold hashSize" + this.hashSize);
  378.             }
  379.             // TODO: this looks suspicious, 26 rather than 24 would be "after" resize
  380.             assertDynamicLengthFits("resize", resize, ivSize + 24 + erdSize, length);
  381.             //
  382.             this.recipientKeyHash = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize, this.hashSize);
  383.             this.keyBlob = Arrays.copyOfRange(data, offset + ivSize + 24 + erdSize + this.hashSize, resize - this.hashSize);

  384.             assertMinimalLength(ivSize + 26 + erdSize + resize + 2, length);
  385.             final int vSize = ZipShort.getValue(data, offset + ivSize + 26 + erdSize + resize);
  386.             if (vSize < 4) {
  387.                 throw new ZipException("Invalid X0017_StrongEncryptionHeader: vSize " + vSize + " is too small to hold CRC");
  388.             }
  389.             // TODO: these offsets look even more suspicious, the constant should likely be 28 rather than 22
  390.             assertDynamicLengthFits("vSize", vSize, ivSize + 22 + erdSize + resize, length);
  391.             //
  392.             this.vData = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize, vSize - 4);
  393.             this.vCRC32 = Arrays.copyOfRange(data, offset + ivSize + 22 + erdSize + resize + vSize - 4, 4);
  394.         }

  395.         // validate values?
  396.     }

  397.     @Override
  398.     public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) throws ZipException {
  399.         super.parseFromCentralDirectoryData(data, offset, length);
  400.         parseCentralDirectoryFormat(data, offset, length);
  401.     }

  402.     @Override
  403.     public void parseFromLocalFileData(final byte[] data, final int offset, final int length) throws ZipException {
  404.         super.parseFromLocalFileData(data, offset, length);
  405.         parseFileFormat(data, offset, length);
  406.     }
  407. }