/* 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.io.DataInput; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.util.Arrays; import freenet.support.Base64; import freenet.support.Fields; import freenet.support.Logger; /** * @author amphibian * * Node-level CHK. Does not have enough information to decode the payload. * But can verify that it is intact. Just has the routingKey. */ public class NodeCHK extends Key { /** 32 bytes for hash, 2 bytes for type */ public static final short FULL_KEY_LENGTH = 34; public NodeCHK(byte[] routingKey2, byte cryptoAlgorithm) { super(routingKey2); if(routingKey2.length != KEY_LENGTH) throw new IllegalArgumentException("Wrong length: "+routingKey2.length+" should be "+KEY_LENGTH); this.cryptoAlgorithm = cryptoAlgorithm; } private NodeCHK(NodeCHK key) { super(key); this.cryptoAlgorithm = key.cryptoAlgorithm; } @Override public Key cloneKey() { return new NodeCHK(this); } public static final int KEY_LENGTH = 32; /** Crypto algorithm */ final byte cryptoAlgorithm; public static final byte BASE_TYPE = 1; @Override public final void writeToDataOutputStream(DataOutputStream stream) throws IOException { write(stream); } @Override public String toString() { return super.toString() + '@' +Base64.encode(routingKey)+ ':' +Integer.toHexString(hash); } @Override public final void write(DataOutput _index) throws IOException { _index.writeShort(getType()); _index.write(routingKey); } public static Key readCHK(DataInput raf, byte algo) throws IOException { byte[] buf = new byte[KEY_LENGTH]; raf.readFully(buf); return new NodeCHK(buf, algo); } @Override public boolean equals(Object key) { if(key == this) return true; if(key instanceof NodeCHK) { NodeCHK chk = (NodeCHK) key; return java.util.Arrays.equals(chk.routingKey, routingKey) && (cryptoAlgorithm == chk.cryptoAlgorithm); } return false; } @Override public int hashCode(){ return super.hashCode(); } @Override public short getType() { return (short) ((BASE_TYPE << 8) + (cryptoAlgorithm & 0xFF)); } @Override public byte[] getRoutingKey(){ return routingKey; } @Override public byte[] getFullKey() { byte[] buf = new byte[FULL_KEY_LENGTH]; short type = getType(); buf[0] = (byte) (type >> 8); buf[1] = (byte) (type & 0xFF); System.arraycopy(routingKey, 0, buf, 2, routingKey.length); return buf; } public static byte cryptoAlgorithmFromFullKey(byte[] fullKey) { return fullKey[1]; } public static byte[] routingKeyFromFullKey(byte[] keyBuf) { if(keyBuf.length == KEY_LENGTH) return keyBuf; if(keyBuf.length != FULL_KEY_LENGTH) { Logger.error(NodeCHK.class, "routingKeyFromFullKey() on "+keyBuf.length+" bytes"); return null; } if(keyBuf[0] != 1 || (keyBuf[1] != Key.ALGO_AES_PCFB_256_SHA256 && keyBuf[1] != Key.ALGO_AES_CTR_256_SHA256)) { if(keyBuf[keyBuf.length-1] == 0 && keyBuf[keyBuf.length-2] == 0) { // We are certain it's a routing-key Logger.minor(NodeCHK.class, "Recovering routing-key stored wrong as full-key (two nulls at end)"); } else { // It might be a routing-key or it might be random data Logger.error(NodeCHK.class, "Maybe recovering routing-key stored wrong as full-key"); } return Arrays.copyOf(keyBuf, KEY_LENGTH); } return Arrays.copyOfRange(keyBuf, 2, 2 + KEY_LENGTH); } @Override public int compareTo(Key arg0) { if(arg0 instanceof NodeSSK) return 1; NodeCHK key = (NodeCHK) arg0; return Fields.compareBytes(routingKey, key.routingKey); } @Override public Key archivalCopy() { return new NodeCHK(this); } }