package org.limewire.security; import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import org.limewire.util.ByteUtils; /** * A token that embeds an IP address and port, allowing one side to * generate a token for a specific IP address/port pair that cannot be easily * guessed. */ public final class AddressSecurityToken extends AbstractSecurityToken { /** As detailed by the GUESS specification * (<a href="https://www.limewire.org/fisheye/viewrep/~raw,r=1.2/limecvs/core/guess_01.html"> * Gnutella UDP Extension for Scalable Searches</a> ). */ public static final int MIN_QK_SIZE_IN_BYTES = 4; /** As detailed by the GUESS specification. */ public static final int MAX_QK_SIZE_IN_BYTES = 16; /** Cached value to make hashCode() much faster. */ private final int _hashCode; /** Generates a <code>AddressSecurityToken</code> for a given * <code>SocketAddress</code>. For a given <code>SocketAddress</code>, * using a different SecretKey and/or SecretPad will result in a different * <code>AddressSecurityToken</code>. * */ public AddressSecurityToken (SocketAddress address, MACCalculatorRepositoryManager mgr) { this(((InetSocketAddress)address).getAddress(), ((InetSocketAddress)address).getPort(), mgr); } /** Generates an <code>AddressSecurityToken</code> for a given IP:Port combo. * For a given IP:Port combo, using a different SecretKey and/or SecretPad * will result in a different <code>AddressSecurityToken</code>. * * @param ip the IP address of the other node * @param port the port of the other node */ public AddressSecurityToken (InetAddress ip, int port, MACCalculatorRepositoryManager mgr) { this(new AddressTokenData(ip,port), mgr); } public AddressSecurityToken(AddressTokenData data,MACCalculatorRepositoryManager mgr) { super(data, mgr); _hashCode = genHashCode(getBytes()); } public AddressSecurityToken(byte[] key, MACCalculatorRepositoryManager mgr) throws InvalidSecurityTokenException { super(key.clone(), mgr); _hashCode = genHashCode(getBytes()); } private int genHashCode(byte [] key) { // TODO: Can't we use Arrays.hashCode(byte[]) ??? // While we have key in the CPU data cache, calculate _hashCode int code = 0x5A5A5A5A; // Mix all bits of key fairly evenly into code for (int i = key.length - 1; i >= 0; --i) { code ^= (0xFF & key[i]); // One-to-one mixing function from RC6 cipher: // f(x) = (2*x*x + x) mod 2**N // We only care about the low-order 32-bits, so there's no // need to use longs to emulate 32-bit unsigned multiply. code = (code * ((code << 1) + 1)); // Left circular shift (rotate) code by 5 bits code = (code >>> 27) | (code << 5); } return code; } @Override protected byte [] getFromMAC(byte [] key, TokenData ignored) { for (int i = key.length - 1; i >= 0; --i) { // The old prepareForNetwork() seemed to leave cobbs encoding to get // of nulls? TODO: is it okay to leave nulls alone? if (key[i] == 0x1c) { key[i] = (byte) (0xFA); } } return key; } public boolean isFor(SocketAddress address) { InetAddress ip = ((InetSocketAddress)address).getAddress(); int port = ((InetSocketAddress)address).getPort(); return isFor(ip, port); } public boolean isFor(InetAddress ip, int port) { return isFor(new AddressTokenData(ip, port)); } @Override public int hashCode() { return _hashCode; } /** Returns a String with the <code>AddressSecurityToken</code> represented * in hexadecimal. */ @Override public String toString() { return "{AddressSecurityToken: " + (new BigInteger(1, getBytes())).toString(16) + "}"; } //-------------------------------------- //--- PUBLIC STATIC CONSTRUCTION METHODS /** * Determines if the bytes are valid for a <code>key</code>. */ public static boolean isValidSecurityTokenBytes(byte[] key) { return key != null && key.length >= MIN_QK_SIZE_IN_BYTES && key.length <= MAX_QK_SIZE_IN_BYTES; } @Override protected boolean isValidBytes(byte [] key) { return isValidSecurityTokenBytes(key); } /** Converts the IP address and port into an encrypted <code>byte[]</code>. */ public static class AddressTokenData implements SecurityToken.TokenData { protected final byte[] data; public AddressTokenData(SocketAddress address) { this(((InetSocketAddress)address).getAddress(), ((InetSocketAddress)address).getPort()); } public AddressTokenData(InetAddress addr, int port) { // get all the input bytes.... byte[] ipBytes = addr.getAddress(); int ipInt = 0; // Load the first 4 bytes into ipInt in little-endian order, // with the twist that any negative bytes end up flipping // all of the higher order bits, but we don't care. for(int i=3; i >= 0; --i) { ipInt ^= ipBytes[i] << (i << 3); } // Start out with 64 bits |0x00|0x00|port(2bytes)|ip(4bytes)| // and encrypt it with our secret key material. data = new byte[8]; ByteUtils.int2beb(port, data, 0); ByteUtils.int2beb(ipInt, data, 4); } public byte [] getData() { return data; } } }