Home | History | Annotate | Download | only in index
      1 /*
      2  * IndexHash
      3  *
      4  * Author: Lasse Collin <lasse.collin (at) tukaani.org>
      5  *
      6  * This file has been put into the public domain.
      7  * You can do whatever you want with this file.
      8  */
      9 
     10 package org.tukaani.xz.index;
     11 
     12 import java.io.InputStream;
     13 import java.io.DataInputStream;
     14 import java.io.IOException;
     15 import java.nio.ByteBuffer;
     16 import java.util.Arrays;
     17 import java.util.zip.CheckedInputStream;
     18 import org.tukaani.xz.common.DecoderUtil;
     19 import org.tukaani.xz.XZIOException;
     20 import org.tukaani.xz.CorruptedInputException;
     21 
     22 public class IndexHash extends IndexBase {
     23     private org.tukaani.xz.check.Check hash;
     24 
     25     public IndexHash() {
     26         super(new CorruptedInputException());
     27 
     28         try {
     29             hash = new org.tukaani.xz.check.SHA256();
     30         } catch (java.security.NoSuchAlgorithmException e) {
     31             hash = new org.tukaani.xz.check.CRC32();
     32         }
     33     }
     34 
     35     public void add(long unpaddedSize, long uncompressedSize)
     36             throws XZIOException {
     37         super.add(unpaddedSize, uncompressedSize);
     38 
     39         ByteBuffer buf = ByteBuffer.allocate(2 * 8);
     40         buf.putLong(unpaddedSize);
     41         buf.putLong(uncompressedSize);
     42         hash.update(buf.array());
     43     }
     44 
     45     public void validate(InputStream in) throws IOException {
     46         // Index Indicator (0x00) has already been read by BlockInputStream
     47         // so add 0x00 to the CRC32 here.
     48         java.util.zip.CRC32 crc32 = new java.util.zip.CRC32();
     49         crc32.update('\0');
     50         CheckedInputStream inChecked = new CheckedInputStream(in, crc32);
     51 
     52         // Get and validate the Number of Records field.
     53         // If Block Header Size was corrupt and became Index Indicator,
     54         // this error would actually be about corrupt Block Header.
     55         // This is why the error message mentions both possibilities.
     56         long storedRecordCount = DecoderUtil.decodeVLI(inChecked);
     57         if (storedRecordCount != recordCount)
     58             throw new CorruptedInputException(
     59                     "XZ Block Header or the start of XZ Index is corrupt");
     60 
     61         // Decode and hash the Index field and compare it to
     62         // the hash value calculated from the decoded Blocks.
     63         IndexHash stored = new IndexHash();
     64         for (long i = 0; i < recordCount; ++i) {
     65             long unpaddedSize = DecoderUtil.decodeVLI(inChecked);
     66             long uncompressedSize = DecoderUtil.decodeVLI(inChecked);
     67 
     68             try {
     69                 stored.add(unpaddedSize, uncompressedSize);
     70             } catch (XZIOException e) {
     71                 throw new CorruptedInputException("XZ Index is corrupt");
     72             }
     73 
     74             if (stored.blocksSum > blocksSum
     75                     || stored.uncompressedSum > uncompressedSum
     76                     || stored.indexListSize > indexListSize)
     77                 throw new CorruptedInputException("XZ Index is corrupt");
     78         }
     79 
     80         if (stored.blocksSum != blocksSum
     81                 || stored.uncompressedSum != uncompressedSum
     82                 || stored.indexListSize != indexListSize
     83                 || !Arrays.equals(stored.hash.finish(), hash.finish()))
     84             throw new CorruptedInputException("XZ Index is corrupt");
     85 
     86         // Index Padding
     87         DataInputStream inData = new DataInputStream(inChecked);
     88         for (int i = getIndexPaddingSize(); i > 0; --i)
     89             if (inData.readUnsignedByte() != 0x00)
     90                 throw new CorruptedInputException("XZ Index is corrupt");
     91 
     92         // CRC32
     93         long value = crc32.getValue();
     94         for (int i = 0; i < 4; ++i)
     95             if (((value >>> (i * 8)) & 0xFF) != inData.readUnsignedByte())
     96                 throw new CorruptedInputException("XZ Index is corrupt");
     97     }
     98 }
     99