// This import is only for the main() example. It is not required by TEAV. import java.math.*; /** * Tiny Encryption Algorithm - Variant. *
* (The following description is from the web page for the C and Assembler source * code at University of Bradford * Yorkshire, England - The Cryptography & Computer Communications Security * Group) The description is used with the permission of the authors, * Dr S J Shepherd and D A G Gillies. *
* The Tiny Encryption Algorithm is one of the fastest and most efficient * cryptographic algorithms in existence. It was developed by David * Wheeler and Roger Needham at the Computer Laboratory of Cambridge * University. It is a Feistel cipher which uses operations from mixed * (orthogonal) algebraic groups - XORs and additions in this case. It * encrypts 64 data bits at a time using a 128-bit key. It seems highly * resistant to differential cryptanalysis, and achieves complete * diffusion (where a one bit difference in the plaintext will cause * approximately 32 bit differences in the ciphertext) after only six * rounds. Performance on a modern desktop computer or workstation is * very impressive. *
* TEA takes 64 bits of data in v[0] and v[1], and 128 bits of key in * k[0] - k[3]. The result is returned in w[0] and w[1]. Returning the * result separately makes implementation of cipher modes other than * Electronic Code Book a little bit easier. *
* TEA can be operated in any of the modes of DES. *
* n is the number of iterations. 32 is ample, 16 is sufficient, as few * as eight should be OK for most applications, especially ones where * the data age quickly (real-time video, for example). The algorithm * achieves good dispersion after six iterations. The iteration count * can be made variable if required. *
* Note this algorithm is optimised for 32-bit CPUs with fast shift * capabilities. It can very easily be ported to assembly language on * most CPUs. *
* delta is chosen to be the Golden ratio ((5/4)1/2 - 1/2 ~ 0.618034) * multiplied by 232. On entry to decipher(), sum is set to be delta * * n. Which way round you call the functions is arbitrary: DK(EK(P)) = * EK(DK(P)) where EK and DK are encryption and decryption under key K * respectively. *
* Translator's notes: *
*
Tiny Encryption Algorithm (TEA)
* TEA is a cryptographic algorithm designed to minimize memory
* footprint, and maximize speed. However, the cryptographers from Counterpane Systems have discovered three related-key
* attacks on TEA, the best of which requires only 223 chosen plaintexts and one related
* key query. The problems arise from the overly simple key schedule. Each TEA key can be
* found to have three other equivalent keys, as described in a paper by David Wagner, John Kelsey, and Bruce Schneier. This precludes the
* possibility of using TEA as a hash function. Roger Needham and David Wheeler have proposed
* extensions to TEA that
* counters the above attacks.
Example of use: *
* byte key[] = new BigInteger("39e858f86df9b909a8c87cb8d9ad599", 16).toByteArray();
* TEAV t = new TEAV(key);
*
* String src = "hello world!";
* System.out.println("input = " + src);
* byte plainSource[] = src.getBytes();
* int enc[] = t.encode(plainSource, plainSource.length);
* System.out.println(t.padding() + " bytes added as padding.");
* byte dec[] = t.decode(enc);
* System.out.println("output = " + new String(dec));
*
*
* @author Translated by Michael Lecuyer (mjl@theorem.com) from the C Language.
* @version 1.0 Sep 13, 2000
* @since JDK1.1
*/
public class TEAV
{
private int _key[]; // The 128 bit key.
private byte _keyBytes[]; // original key as found
private int _padding; // amount of padding added in byte --> integer conversion.
/**
* Array of hex char mappings.
* hex[0] = '0', hex[15] = 'F'.
*/
protected static final char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* Encodes and decodes "Hello world!" for your personal pleasure.
*/
public static void main(String args[])
{
// A simple test of TEAV.
String keyString = "39e858f86df9b909a8c87cb8d9ad599";
byte key[] = new BigInteger(keyString, 16).toByteArray();
TEAV t = new TEAV(key);
String src = "hello world!";
System.out.println("input = [" + src + "]");
// Pad the plaintext with spaces.
src = t.padPlaintext(src);
// Get bytes from the text and encode it.
byte plainSource[] = src.getBytes();
int enc[] = t.encode(plainSource, plainSource.length);
// Report on padding, it should be zero since we originally padded the string with spaces.
System.out.println(t.padding() + " bytes added as padding.");
// Display what the encoding would be in a hex string.
String hexStr = t.binToHex(enc);
System.out.println("Encoding as Hex string: " + hexStr);
// Just to prove that converting it to a hex string a back works decode the hex string.
int enc2[] = t.hexToBin(hexStr);
byte dec[] = t.decode(enc2);
// Display the resulting decoded string, trimmed.to remove padding.
System.out.println("output = [" + new String(dec).trim() + "]");
System.exit(0);
}
/**
* Accepts key for enciphering/deciphering.
*
* @throws ArrayIndexOutOfBoundsException if the key isn't the correct length.
*
* @param key 128 bit (16 byte) key.
*/
public TEAV(byte key[])
{
int klen = key.length;
_key = new int[4];
// Incorrect key length throws exception.
if (klen != 16)
throw new ArrayIndexOutOfBoundsException(this.getClass().getName() + ": Key is not 16 bytes");
int j, i;
for (i = 0, j = 0; j < klen; j += 4, i++)
_key[i] = (key[j] << 24 ) | (((key[j+1])&0xff) << 16) | (((key[j+2])&0xff) << 8) | ((key[j+3])&0xff);
_keyBytes = key; // save for toString.
}
public TEAV(int key[])
{
_key = key;
}
/**
* Representation of TEA class
*/
public String toString()
{
String tea = this.getClass().getName();
tea += ": Tiny Encryption Algorithm (TEA) key: " + getHex(_keyBytes);
return tea;
}
/**
* Encipher two ints.
* Replaces the original contents of the parameters with the results.
* The integers are usually created from 8 bytes.
* The usual way to collect bytes to the int array is:
*
* byte ba[] = { .... };
* int v[] = new int[2];
* v[0] = (ba[j] << 24 ) | (((ba[j+1])&0xff) << 16) | (((ba[j+2])&0xff) << 8) | ((ba[j+3])&0xff);
* v[1] = (ba[j+4] << 24 ) | (((ba[j+5])&0xff) << 16) | (((ba[j+6])&0xff) << 8) | ((ba[j+7])&0xff);
* v = encipher(v);
*
*
* @param v two int array as input.
*
* @return array of two ints, enciphered.
*/
public int [] encipher(int v[])
{
int y= v[0];
int z= v[1];
int sum=0;
int delta=0x9E3779B9;
int n=32;
while(n-->0)
{
y += (z << 4 ^ z >>> 5) + z ^ sum + _key[(int)(sum&3)];
sum += delta;
z += (y << 4 ^ y >>> 5) + y ^ sum + _key[(int)(sum >>>11) & 3];
}
int w[] = new int[2];
w[0] = (int)y;
w[1] = (int)z;
return w;
}
/**
* Decipher two ints.
* Replaces the original contents of the parameters with the results.
* The integers are usually decocted to 8 bytes.
* The decoction of the ints to bytes can be done
* this way.
* * int x[] = decipher(ins); * outb[j] = (byte)(x[0] >>> 24); * outb[j+1] = (byte)(x[0] >>> 16); * outb[j+2] = (byte)(x[0] >>> 8); * outb[j+3] = (byte)(x[0]); * outb[j+4] = (byte)(x[1] >>> 24); * outb[j+5] = (byte)(x[1] >>> 16); * outb[j+6] = (byte)(x[1] >>> 8); * outb[j+7] = (byte)(x[1]); ** * @param v
int array of 2
*
* @return deciphered int array of 2
*/
public int [] decipher(int v[])
{
int y=v[0];
int z=v[1];
int sum=0xC6EF3720;
int delta=0x9E3779B9;
int n=32;
// sum = delta<<5, in general sum = delta * n
while (n-->0)
{
z -= (y << 4 ^ y >>> 5) + y ^ sum + _key[(sum>>>11) & 3];
sum -= delta;
y -= (z << 4 ^ z >>> 5) + z ^ sum + _key[sum &3];
}
int w[] = new int[2];
w[0] = (int)y;
w[1] = (int)z;
return w;
}
/**
* Byte wrapper for encoding.
* Converts bytes to ints.
* Padding will be added if required.
*
* @param b incoming byte array
*
* @param byte count
*
* @return integer conversion array, possibly with padding.
*
* @see #padding
*/
public int [] encode(byte b[], int count)
{
int j ,i;
int bLen = count;
byte bp[] = b;
_padding = bLen % 8;
if (_padding != 0) // Add some padding, if necessary.
{
_padding = 8 - (bLen % 8);
bp = new byte[bLen + _padding];
System.arraycopy(b, 0, bp, 0, bLen);
bLen = bp.length;
}
int intCount = bLen / 4;
int r[] = new int[2];
int out[] = new int[intCount];
for (i = 0, j = 0; j < bLen; j += 8, i += 2)
{
r[0] = (bp[j] << 24 ) | (((bp[j+1])&0xff) << 16) | (((bp[j+2])&0xff) << 8) | ((bp[j+3])&0xff);
r[1] = (bp[j+4] << 24 ) | (((bp[j+5])&0xff) << 16) | (((bp[j+6])&0xff) << 8) | ((bp[j+7])&0xff);
r = encipher(r);
out[i] = r[0];
out[i+1] = r[1];
}
return out;
}
/**
* Report how much padding was done in the last encode.
*
* @return bytes of padding added
*
* @see #encode
*/
public int padding()
{
return _padding;
}
/**
* Convert a byte array to ints and then decode.
* There may be some padding at the end of the byte array from
* the previous encode operation.
*
* @param b bytes to decode
* @param count number of bytes in the array to decode
*
* @return byte array of decoded bytes.
*/
public byte [] decode(byte b[], int count)
{
int i, j;
int intCount = count / 4;
int ini[] = new int[intCount];
for (i = 0, j = 0; i < intCount; i += 2, j += 8)
{
ini[i] = (b[j] << 24 ) | (((b[j+1])&0xff) << 16) | (((b[j+2])&0xff) << 8) | ((b[j+3])&0xff);
ini[i+1] = (b[j+4] << 24 ) | (((b[j+5])&0xff) << 16) | (((b[j+6])&0xff) << 8) | ((b[j+7])&0xff);
}
return decode(ini);
}
/**
* Decode an integer array.
* There may be some padding at the end of the byte array from
* the previous encode operation.
*
* @param b bytes to decode
* @param count number of bytes in the array to decode
*
* @return byte array of decoded bytes.
*/
public byte [] decode(int b[])
{
// create the large number and start stripping ints out, two at a time.
int intCount = b.length;
byte outb[] = new byte[intCount * 4];
int tmp[] = new int[2];
// decipher all the ints.
int i, j;
for (j = 0, i = 0; i < intCount; i += 2, j += 8)
{
tmp[0] = b[i];
tmp[1] = b[i+1];
tmp = decipher(tmp);
outb[j] = (byte)(tmp[0] >>> 24);
outb[j+1] = (byte)(tmp[0] >>> 16);
outb[j+2] = (byte)(tmp[0] >>> 8);
outb[j+3] = (byte)(tmp[0]);
outb[j+4] = (byte)(tmp[1] >>> 24);
outb[j+5] = (byte)(tmp[1] >>> 16);
outb[j+6] = (byte)(tmp[1] >>> 8);
outb[j+7] = (byte)(tmp[1]);
}
return outb;
}
/**
* Convert a string into an integer array form suitable for decoding.
*
* @param hexStr String of hexadecimal digits.
* @return integer array.
* @throws ArrayIndexOutOfBoundsException if the string length is not divisible into integer length pieces.
*/
public int [] hexToBin(String hexStr) throws ArrayIndexOutOfBoundsException
{
int hexStrLen = hexStr.length();
// Decode a hex string into a collection of ints.
if ((hexStrLen % 8) != 0)
throw new ArrayIndexOutOfBoundsException("Hex string has incorrect length, required to be divisible by eight: " + hexStrLen);
int outLen = hexStrLen / 8;
int out[] = new int[outLen];
byte nibble[] = new byte[2];
byte b[] = new byte[4];
int posn = 0;
for (int i = 0; i < outLen; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 2; k++)
{
switch (hexStr.charAt(posn++))
{
case '0': nibble[k] = (byte)0; break;
case '1': nibble[k] = (byte)1; break;
case '2': nibble[k] = (byte)2; break;
case '3': nibble[k] = (byte)3; break;
case '4': nibble[k] = (byte)4; break;
case '5': nibble[k] = (byte)5; break;
case '6': nibble[k] = (byte)6; break;
case '7': nibble[k] = (byte)7; break;
case '8': nibble[k] = (byte)8; break;
case '9': nibble[k] = (byte)9; break;
case 'A': nibble[k] = (byte)0xA; break;
case 'B': nibble[k] = (byte)0xB; break;
case 'C': nibble[k] = (byte)0xC; break;
case 'D': nibble[k] = (byte)0xD; break;
case 'E': nibble[k] = (byte)0xE; break;
case 'F': nibble[k] = (byte)0xF; break;
case 'a': nibble[k] = (byte)0xA; break;
case 'b': nibble[k] = (byte)0xB; break;
case 'c': nibble[k] = (byte)0xC; break;
case 'd': nibble[k] = (byte)0xD; break;
case 'e': nibble[k] = (byte)0xE; break;
case 'f': nibble[k] = (byte)0xF; break;
}
}
b[j] = (byte)(nibble[0] << 4 | nibble[1]);
}
out[i] = (b[0] << 24 ) | (((b[1])&0xff) << 16) | (((b[2])&0xff) << 8) | ((b[3])&0xff);
}
return out;
}
/**
* Convert an array of ints into a hex string.
*
* @param enc Array of integers.
* @return String hexadecimal representation of the integer array.
* @throws ArrayIndexOutOfBoundsException if the array doesn't contain pairs of integers.
*/
public String binToHex(int enc[]) throws ArrayIndexOutOfBoundsException
{
// The number of ints should always be a multiple of two as required by TEA (64 bits).
if ((enc.length % 2) == 1)
throw new ArrayIndexOutOfBoundsException("Odd number of ints found: " + enc.length);
StringBuffer sb = new StringBuffer();
byte outb[] = new byte[8];
int tmp[] = new int[2];
int counter = enc.length / 2;
for (int i = 0; i < enc.length; i += 2)
{
outb[0] = (byte)(enc[i] >>> 24);
outb[1] = (byte)(enc[i] >>> 16);
outb[2] = (byte)(enc[i] >>> 8);
outb[3] = (byte)(enc[i]);
outb[4] = (byte)(enc[i+1] >>> 24);
outb[5] = (byte)(enc[i+1] >>> 16);
outb[6] = (byte)(enc[i+1] >>> 8);
outb[7] = (byte)(enc[i+1]);
sb.append(getHex(outb));
}
return sb.toString();
}
// Display some bytes in HEX.
//
public String getHex(byte b[])
{
StringBuffer r = new StringBuffer();
for (int i = 0; i < b.length; i++)
{
int c = ((b[i]) >>> 4) & 0xf;
r.append(hex[c]);
c = ((int)b[i] & 0xf);
r.append(hex[c]);
}
return r.toString();
}
/**
* Pad a string out to the proper length with the given character.
*
* @param str Plain text string.
* @param pc Padding character.
*/
public String padPlaintext(String str, char pc)
{
StringBuffer sb = new StringBuffer(str);
int padding = sb.length() % 9;
for (int i = 0; i < padding; i++)
sb.append(pc);
return sb.toString();
}
/**
* Pad a string out to the proper length with spaces.
*
* @param str Plain text string.
*/
public String padPlaintext(String str)
{
return padPlaintext(str, ' ');
}
}