• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * IndexHash
3  *
4  * Author: Lasse Collin <lasse.collin@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 
IndexHash()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 
add(long unpaddedSize, long uncompressedSize)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 
validate(InputStream in)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         long storedRecordCount = DecoderUtil.decodeVLI(inChecked);
54         if (storedRecordCount != recordCount)
55             throw new CorruptedInputException("XZ Index is corrupt");
56 
57         // Decode and hash the Index field and compare it to
58         // the hash value calculated from the decoded Blocks.
59         IndexHash stored = new IndexHash();
60         for (long i = 0; i < recordCount; ++i) {
61             long unpaddedSize = DecoderUtil.decodeVLI(inChecked);
62             long uncompressedSize = DecoderUtil.decodeVLI(inChecked);
63 
64             try {
65                 stored.add(unpaddedSize, uncompressedSize);
66             } catch (XZIOException e) {
67                 throw new CorruptedInputException("XZ Index is corrupt");
68             }
69 
70             if (stored.blocksSum > blocksSum
71                     || stored.uncompressedSum > uncompressedSum
72                     || stored.indexListSize > indexListSize)
73                 throw new CorruptedInputException("XZ Index is corrupt");
74         }
75 
76         if (stored.blocksSum != blocksSum
77                 || stored.uncompressedSum != uncompressedSum
78                 || stored.indexListSize != indexListSize
79                 || !Arrays.equals(stored.hash.finish(), hash.finish()))
80             throw new CorruptedInputException("XZ Index is corrupt");
81 
82         // Index Padding
83         DataInputStream inData = new DataInputStream(inChecked);
84         for (int i = getIndexPaddingSize(); i > 0; --i)
85             if (inData.readUnsignedByte() != 0x00)
86                 throw new CorruptedInputException("XZ Index is corrupt");
87 
88         // CRC32
89         long value = crc32.getValue();
90         for (int i = 0; i < 4; ++i)
91             if (((value >>> (i * 8)) & 0xFF) != inData.readUnsignedByte())
92                 throw new CorruptedInputException("XZ Index is corrupt");
93     }
94 }
95