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