package com.limegroup.gnutella.guess; import java.util.Arrays; import java.net.InetAddress; import java.security.SecureRandom; import com.limegroup.gnutella.security.SecurityUtils; /* package */ class TEAQueryKeyGenerator implements QueryKeyGenerator { // This uses TEA (Tiny Encryption Algorithm) from // D. Wheeler and R. Needham of Cambridge University. // TEA is believed to be stronger than the DES cipher // used in the previous LW QK algorithm, is much faster // in software, and has an extremely small footprint in // the CPU's data and instruction caches. // Length of each query key that we generate, in bytes. protected final int QK_LENGTH = 4; // Pre- and post- encryption cyclic shift values // to get random padding equivalent to the previous // DES implementation. protected final int PRE_ROTATE; protected final int POST_ROTATE; // TEA encryption keys protected final int LK0; protected final int LK1; protected final int RK0; protected final int RK1; // Keys for "whitening" the encryption block before and // after encryption. protected final long PRE_WHITEN_KEY; protected final long POST_WHITEN_KEY; /* package */ TEAQueryKeyGenerator() { SecureRandom rand = SecurityUtils.createSecureRandomNoBlock(); PRE_WHITEN_KEY = rand.nextLong(); POST_WHITEN_KEY = rand.nextLong(); LK0 = rand.nextInt(); LK1 = rand.nextInt(); RK0 = rand.nextInt(); RK1 = rand.nextInt(); int rotations = rand.nextInt(); PRE_ROTATE = rotations & 0x3F; // Low 6 bits POST_ROTATE = rotations >>> 26; // High 6 bits } // Constructor is package scoped for unit tests /* package */ TEAQueryKeyGenerator(int k0, int k1, int k2, int k3, int preRotate, int postRotate) { // Set cyclic shifts to allow getting at differet // parts of the output PRE_ROTATE = preRotate & 0x3F; POST_ROTATE = postRotate & 0x3F; // No whitening, to expose TEA to tests PRE_WHITEN_KEY = POST_WHITEN_KEY = 0; // Use given TEA keys LK0 = k0; LK1 = k1; RK0 = k2; RK1 = k3; } /** Checks that this instance was used to create keyBytes from ip and port.*/ public boolean checkKeyBytes(byte[] keyBytes, InetAddress ip, int port) { // This only works because this.getKeyBytes(ip,port) is deterministic. return Arrays.equals(keyBytes, getKeyBytes(ip, port)); } /** Returns the raw bytes for a QueryKey, which will not contain * 0x1C and 0x00, to accomidate clients that poorly parse GGEP. */ public byte[] getKeyBytes(InetAddress ip, int port) { byte[] toEncrypt = new byte[8]; // get all the input bytes.... byte[] ipBytes = ip.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. long key64 = encrypt(((long)port) << 32) | (ipInt & 0xFFFFFFFFL); // 32-bit QK gives attackers the least amount of information // about our secret key while still not making it worth their // while to try and make a botnet out of the Gnutella network byte[] qkBytes = new byte[QK_LENGTH]; // Copy bytes that arent 0x00 or 0x1C into output int outIndex = QK_LENGTH - 1; for (int left=(int)(key64 >> 32), right=(int)key64; outIndex>=0; left >>>= 8) { if (left == 0) { // get more data left = right; right = ~right; // This ensures loop termination // Worst case is 0x1CnNnNnN 0x1CnNnNnN, where nN is 0 or 1C. } int lowByte = left & 0xFF; if (lowByte != 0 && lowByte != 0x1C) { qkBytes[outIndex] = (byte) lowByte; --outIndex; } } return qkBytes; } /** * Encrypts a 64-bit value using the TEA block cipher. * * The role previously played by the variable padding * is now done by pre- and post-encryption cyclic * shifting, as well as pre- and post-encryption whitening. * * @param block the 64-bit block to be encrypted * @return block encrypted using the secret key material * within this class. */ private final long encrypt(long block) { block = (block << PRE_ROTATE) | (block >>> (64 - PRE_ROTATE)); // Pre-encryption whitening block ^= PRE_WHITEN_KEY; // 32 cycle (a.k.a. 64 Feistel round) TEA encryption int left = (int) (block >> 32); int right = (int) block; for (int cycleCount = 32, roundKey = 0; cycleCount > 0; --cycleCount) { roundKey += 0x9E3779B9; left += ((right<<4)+LK0) ^ (right+roundKey) ^ ((right>>>5)+LK1); right += (( left<<4)+RK0) ^ ( left+roundKey) ^ (( left>>>5)+RK1); } block = (((long) left) << 32) | (right & 0xFFFFFFFFL); // Post-encryption whitening block ^= POST_WHITEN_KEY; // Post-encryption cyclic shift return (block << POST_ROTATE) | (block >>> (64 - POST_ROTATE)); } }