/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.keys; import java.security.MessageDigest; import java.util.Arrays; import freenet.crypt.SHA256; import freenet.support.Fields; /** * @author amphibian * * CHK plus data. When fed a ClientCHK, can decode into the original * data for a client. */ public class CHKBlock implements KeyBlock { final byte[] data; final byte[] headers; final short hashIdentifier; final NodeCHK chk; final int hashCode; public static final int MAX_LENGTH_BEFORE_COMPRESSION = Integer.MAX_VALUE; public static final int TOTAL_HEADERS_LENGTH = 36; public static final int DATA_LENGTH = 32768; /* Maximum length of compressed payload */ public static final int MAX_COMPRESSED_DATA_LENGTH = DATA_LENGTH - 4; @Override public String toString() { return super.toString()+": chk="+chk; } /** * @return The header for this key. DO NOT MODIFY THIS DATA! */ public byte[] getHeaders() { return headers; } /** * @return The actual data for this key. DO NOT MODIFY THIS DATA! */ public byte[] getData() { return data; } public static CHKBlock construct(byte[] data, byte[] header, byte cryptoAlgorithm) throws CHKVerifyException { return new CHKBlock(data, header, null, true, cryptoAlgorithm); } public CHKBlock(byte[] data2, byte[] header2, NodeCHK key) throws CHKVerifyException { this(data2, header2, key, key.cryptoAlgorithm); } public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, byte cryptoAlgorithm) throws CHKVerifyException { this(data2, header2, key, true, cryptoAlgorithm); } public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify, byte cryptoAlgorithm) throws CHKVerifyException { data = data2; headers = header2; if(headers.length != TOTAL_HEADERS_LENGTH) throw new IllegalArgumentException("Wrong length: "+headers.length+" should be "+TOTAL_HEADERS_LENGTH); hashIdentifier = (short)(((headers[0] & 0xff) << 8) + (headers[1] & 0xff)); // Logger.debug(CHKBlock.class, "Data length: "+data.length+", header length: "+header.length); if((key != null) && !verify) { this.chk = key; hashCode = key.hashCode() ^ Fields.hashCode(data) ^ Fields.hashCode(headers) ^ cryptoAlgorithm; return; } // Minimal verification // Check the hash if(hashIdentifier != HASH_SHA256) throw new CHKVerifyException("Hash not SHA-256"); MessageDigest md = SHA256.getMessageDigest(); md.update(headers); md.update(data); byte[] hash = md.digest(); SHA256.returnMessageDigest(md); if(key == null) { chk = new NodeCHK(hash, cryptoAlgorithm); } else { chk = key; byte[] check = chk.routingKey; if(!java.util.Arrays.equals(hash, check)) { throw new CHKVerifyException("Hash does not verify"); } // Otherwise it checks out } hashCode = chk.hashCode() ^ Fields.hashCode(data) ^ Fields.hashCode(headers) ^ cryptoAlgorithm; } @Override public NodeCHK getKey() { return chk; } @Override public byte[] getRawHeaders() { return headers; } @Override public byte[] getRawData() { return data; } @Override public byte[] getPubkeyBytes() { return null; } @Override public byte[] getFullKey() { return getKey().getFullKey(); } @Override public byte[] getRoutingKey() { return getKey().getRoutingKey(); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object o) { if(!(o instanceof CHKBlock)) return false; CHKBlock block = (CHKBlock) o; if(!chk.equals(block.chk)) return false; if(!Arrays.equals(data, block.data)) return false; if(!Arrays.equals(headers, block.headers)) return false; if(hashIdentifier != block.hashIdentifier) return false; return true; } }