package org.limewire.security; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Random; import junit.framework.Test; import org.limewire.util.BaseTestCase; import org.limewire.util.ByteUtils; import org.limewire.util.PrivilegedAccessor; public class TEAMACCalculatorTest extends BaseTestCase { public TEAMACCalculatorTest(String name) { super(name); } public static Test suite() { return buildTestSuite(TEAMACCalculatorTest.class); } // Generates 420 QueryKeys using random secret keys // and makes sure none of them contain 0x00 or 0x1C public void testAllBytesNetworkSafe() throws Exception { Random rand = new Random(); InetAddress[] addresses = new InetAddress[20]; byte[] randAddressBytes = new byte[4]; for(int i=addresses.length-1; i >= 0; --i) { rand.nextBytes(randAddressBytes); addresses[i] = InetAddress.getByAddress(randAddressBytes); } for(int i=20; i > 0; --i) { TEAMACCalculator key = new TEAMACCalculator(); for(int j=addresses.length-1; j >= 0; --j) { byte[] keyBytes = key.getMACBytes(new AddressSecurityToken.AddressTokenData(addresses[j], 1024 + i)); for(int k=keyBytes.length-1; k >= 0; --k) { int keyByte = keyBytes[k]; if (keyByte == 0x00 || keyByte == 0x1C) { byte[] printBuf = new byte[keyBytes.length+1]; System.arraycopy(keyBytes, 0, printBuf, 1, keyBytes.length); printBuf[0] = (byte) 1; fail("keyBytes contains illegal byte "+ (new BigInteger(1,printBuf)).toString(16)); } } } } } // Generates 420 QueryKeys using random secret keys // and makes sure that they all can be verified by // the QueryKeyGenerator that created them public void testKeyVerification() throws Exception { Random rand = new Random(); InetAddress[] addresses = new InetAddress[20]; byte[] randAddressBytes = new byte[4]; for(int i=addresses.length-1; i >= 0; --i) { rand.nextBytes(randAddressBytes); addresses[i] = InetAddress.getByAddress(randAddressBytes); } for(int i=20; i > 0; --i) { TEAMACCalculator key = new TEAMACCalculator(); for(int j=addresses.length-1; j >= 0; --j) { byte[] keyBytes = key.getMACBytes(new AddressSecurityToken.AddressTokenData(addresses[j], 1024 + i)); if (! Arrays.equals(keyBytes, key.getMACBytes(new AddressSecurityToken.AddressTokenData(addresses[j], 1024 + i)))) { byte[] printBuf = new byte[keyBytes.length+1]; System.arraycopy(keyBytes, 0, printBuf, 1, keyBytes.length); printBuf[0] = (byte) 1; fail("keyBytes fails to verify "+ (new BigInteger(1,printBuf)).toString(16)); } } } } // Breaks abstraction, but ensures that TEA is implemented correctly public void testTEAtestVectors() throws Exception { TEAVectorTester key = new TEAVectorTester(0,0,0,0,0,0); long cipherBlock = key.publicEncrypt(0x0L); assertEquals("TEA test vecotr failed", 0x41EA3A0A94BAA940L, cipherBlock); assertEquals("TEA test vector failed.", 0x41EA3A0A, key.encrypt(0,0)); // get at the other half of the block and test the 64-bit rotation code key = new TEAVectorTester(0,0,0,0,0,32); assertEquals("TEA test vector failed.", 0x94BAA940, key.encrypt(0,0)); } /** * This test verifies the following identity: * * Let E be the CBC-CMAC encryption * m1 and m2 two messages and * t1 = E(m1), t2 = E(m2) * * then the following holds: * * E(concat(m, t1 ^ m2)) = t2 * */ public void testEncryptCBCMACIdentity() { TEAMACCalculator generator = new TEAMACCalculator(); Random random = new Random(); for (int i = 0; i < 100; i++) { byte[] msg1 = new byte[8]; random.nextBytes(msg1); byte[] msg2 = new byte[8]; random.nextBytes(msg2); long tag1 = generator.encryptCBCCMAC(msg1); long tag2 = generator.encryptCBCCMAC(msg2); byte[] concat = new byte[16]; System.arraycopy(msg1, 0, concat, 0, msg1.length); byte[] tag1Inbytes = new byte[8]; ByteUtils.long2leb(tag1, tag1Inbytes, 0); System.arraycopy(xor(tag1Inbytes, msg2), 0, concat, msg1.length, msg1.length); assertEquals(tag2, generator.encryptCBCCMAC(concat)); } } private static byte[] xor(byte a1, byte... a2) { return xor(new byte[] { a1 }, a2); } private static byte[] xor(byte[] a1, byte... a2) { byte[] xored = new byte[a1.length]; for (int i = 0; i < a1.length; i++) { xored[i] = (byte) (a1[i] ^ a2[i]); } return xored; } public void testXor() { assertEquals(new byte[4], xor(new byte[4], new byte[4])); assertEquals(new byte[] { 1 }, xor((byte)1, (byte)0)); assertEquals(new byte[] { 0 }, xor((byte)1, (byte)1)); } public void testEncryptCBCMACIterations() { // a single iteration on input of length <= 8 should return // the same as encrypt TEAMACCalculator generator = new TEAMACCalculator(); Random random = new Random(); for (int i = 1; i <= 8; i++) { byte[] data = new byte[i]; random.nextBytes(data); long asLong = ByteUtils.leb2long(data, 0, data.length); assertEquals(generator.encrypt(asLong), generator.encryptCBCCMAC(data)); } // ensure multiple iterations are executed and all data is processed for (int i = 9; i <= 16; i++) { byte[] data = new byte[i]; random.nextBytes(data); long asLong = ByteUtils.leb2long(data, 0, 8); assertNotSame(generator.encrypt(asLong), generator.encryptCBCCMAC(data)); } // ensure multiple iterations are executed and all data is processed for (int i = 9; i <= 16; i++) { byte[] data = new byte[i]; random.nextBytes(data); long asLong = ByteUtils.leb2long(data, 0, 8); long asLong2 = ByteUtils.leb2long(data, 8, data.length - 8); assertEquals(generator.encrypt(generator.encrypt(asLong) ^ asLong2), generator.encryptCBCCMAC(data)); } } private static class TEAVectorTester extends TEAMACCalculator { /** Set up a tester with given TEA encryption keys */ public TEAVectorTester(int k0, int k1, int k2, int k3, int preRotate, int postRotate) { super(k0,k1,k2,k3, preRotate, postRotate); } /** In the absence of 0x00 and 0x1C bytes, returns the * left int of the TEA encryption block after encrypting (left,right). * Use the postRotate to get at different parts of the TEA encryption * output. */ public int encrypt(int left, int right) throws UnknownHostException { // Prepare right for the unusual encoding // of IP addresses as ints used in QueryKeyGenerator for(int i=0x80; i>0; i <<= 8) { if ((right & i) != 0) { right = (~right) ^ (i-1) ^ i; } } byte[] ipBytes = new byte[4]; for(int i=0; i <= 3; ++i) { ipBytes[i] = (byte) right; right >>>= 8; } byte[] resultBytes = getMACBytes(new AddressSecurityToken.AddressTokenData(InetAddress.getByAddress(ipBytes), left)); int byteCount = resultBytes.length; int result = 0; for(int i=byteCount-4; i<byteCount ; ++i) { result <<= 8; result |= 0xFF & resultBytes[i]; } return result; } // Accesses the private method super.encrypt(long) public long publicEncrypt(long arg) throws NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException { Long result = (Long) PrivilegedAccessor.invokeMethod(this, "encrypt", new Object[] {arg}, new Class[] { long.class }); return result; } } }