Source code: com/ssttr/crypto/TEA.java
1 /*
2 * TEA.java
3 *
4 * This code is considered Public Domain based on the statement provided with the original code.
5 * See the comments bellow for more details.
6 *
7 * Created on May 2, 2003, 11:46 PM
8 */
9
10 package com.ssttr.crypto;
11
12 import com.ssttr.util.Strings;
13
14 /**
15 * This is a 100% Pure Java implementation of the Tiny Encryption Algorithm which
16 * can be found <a href="http://vader.brad.ac.uk/tea/tea.shtml">http://vader.brad.ac.uk/tea/tea.shtml</a>.
17 * Or at least that's where I originaly found it. It appears that it is still available
18 * here: <a href="http://www.simonshepherd.supanet.com/tea.htm">http://www.simonshepherd.supanet.com/tea.htm</a>.
19 * <p>
20 * TEA is only capable of using 128 bit keys. If any other key length is desired
21 * you will have to pad or chop (as appropriate) the key to be 128 bits long.
22 * <p>
23 * It was ported from the ANSI C new variant <a href="http://vader.brad.ac.uk/tea/source.shtml#new_ansi">http://vader.brad.ac.uk/tea/source.shtml#new_ansi</a>.
24 * As I mentioned above, that page seems to have disappeard, so here's the new equivelent:
25 * <a href="http://www.simonshepherd.supanet.com/source.htm#new_ansi">http://www.simonshepherd.supanet.com/source.htm#new_ansi</a>.
26 * In case the original page goes down it's copied below:
27 * <p>
28 * Please feel free to use any of this code in your applications. The TEA
29 * algorithm (including new-variant TEA) has been placed in the public domain, as have my assembly
30 * language implementations. <p><hr>
31 * <p>
32 * <pre>
33 * void encipher(const unsigned long *const v,unsigned long *const w,
34 * const unsigned long * const k)
35 * {
36 * register unsigned long y=v[0],z=v[1],sum=0,delta=0x9E3779B9,
37 * a=k[0],b=k[1],c=k[2],d=k[3],n=32;
38 *
39 * while(n-->0)
40 * {
41 * sum += delta;
42 * y += (z << 4)+a ^ z+sum ^ (z >> 5)+b;
43 * z += (y << 4)+c ^ y+sum ^ (y >> 5)+d;
44 * }
45 *
46 * w[0]=y; w[1]=z;
47 * }
48 *
49 * void decipher(const unsigned long *const v,unsigned long *const w,
50 * const unsigned long * const k)
51 * {
52 * register unsigned long y=v[0],z=v[1],sum=0xC6EF3720,
53 * delta=0x9E3779B9,a=k[0],b=k[1],
54 * c=k[2],d=k[3],n=32;
55 *
56 * // sum = delta<<5, in general sum = delta * n
57 *
58 * while(n-->0)
59 * {
60 * z -= (y << 4)+c ^ y+sum ^ (y >> 5)+d;
61 * y -= (z << 4)+a ^ z+sum ^ (z >> 5)+b;
62 * sum -= delta;
63 * }
64 *
65 * w[0]=y; w[1]=z;
66 * }
67 *
68 *
69 * </pre>
70 * @author smeiners
71 */
72
73 public class TEA
74 implements SKEncryption
75 {
76 /** Default number of iterations to perform durring encryption/decryption (32). */
77 public static final int DEFAULT_ITERATIONS = 32;
78
79 private int[] key = null;
80 private static final int delta = 0x9E3779B9;
81 private static final int SUM = 0xC6EF3720;
82 private int cycles = DEFAULT_ITERATIONS;
83
84 /** Creates a new instance of TEA */
85 public TEA ()
86 {
87 }
88
89 /**
90 * Creates a new instance of TEA.
91 *
92 * @param cycles The number of iterations to perform durring encryption/decryption. If iterations < 6 the default will be used.
93 */
94 public TEA (int iterations)
95 {
96 if( iterations >= 6 )
97 cycles = iterations;
98 }
99
100 public int getIterations()
101 { return cycles; }
102
103 /**
104 * Sets the encryption key to be used.
105 *
106 * @param key The key as a hexidecimal string.
107 * @throws IllegalArgumentException if <code>key.length() != 32</code>.
108 */
109 public void setHexKey(String key)
110 {
111 setKey( Strings.fromHex(key) );
112 }
113
114 /**
115 * Sets the encryption key to be used.
116 *
117 * @param key The key in raw (non-hex) byte form.
118 * @throws IllegalArgumentException if <code>key.length != 16</code>.
119 */
120 public void setKey (byte[] key)
121 {
122 if( key.length != 16 )
123 throw new IllegalArgumentException("key.length != 16");
124
125 this.key = toInts(key);
126 }
127
128 /**
129 * Encrypts some data.
130 *
131 * @param rawData The data to be encrypted (binary data is ok).
132 * @return Encrypted data.
133 * @throws IllegalArgumentException if rawData.length == 0
134 */
135 public byte[] encrypt (byte[] rawData)
136 {
137 if( rawData.length == 0 )
138 throw new IllegalArgumentException("rawData.length == 0");
139
140 if( rawData.length % 8 != 0 )
141 {
142 // pad the data out to the 8 byte boundry with 0's
143 byte[] tmp = new byte[ rawData.length + ( 8 - (rawData.length % 8) ) ];
144 System.arraycopy( rawData, 0, tmp, 0, rawData.length );
145 for( int i = rawData.length; i < tmp.length; i ++ )
146 tmp[i] = 0;
147 rawData = tmp;
148 }
149
150 int[] in = toInts(rawData);
151 int[] out = new int[in.length];
152
153 for( int i = in.length - 2; i >= 0; i -= 2 )
154 encipher(in, out, i);
155
156 return toBytes(out);
157 }
158
159 /**
160 * Decrypts some data.
161 *
162 * @param cryptedData The data to be decrypted.
163 * @return The original rawData that was previously encrypted.
164 * @throws IllegalArgumentException if cryptedData.length == 0
165 */
166 public byte[] decrypt (byte[] cryptedData)
167 {
168 if( cryptedData.length == 0 )
169 throw new IllegalArgumentException("cryptedData.length == 0");
170
171 int[] in = toInts(cryptedData);
172 int[] out = new int[in.length];
173
174 for( int i = in.length - 2; i >= 0; i -= 2 )
175 decipher(in, out, i);
176
177 byte[] data = toBytes(out);
178
179 int z = data.length - 1;
180
181 if( data[z] != 0 )
182 return data;
183
184 for( ; data[z] == 0 && z >= 0; z -- ); // find the end of the 0 padding
185
186 z++; // add one to change the index to the length
187
188 byte[] u = new byte[z];
189 System.arraycopy(data, 0, u, 0, z);
190
191 return u;
192 }
193
194 private final void encipher(final int[] in, final int[] out, final int offset)
195 {
196 int y = in[offset],
197 z = in[offset+1],
198 sum = 0,
199 n = cycles;
200
201 while( n-- > 0 )
202 {
203 y += (z << 4 ^ z >> 5) + z ^ sum + key[sum&3];
204 sum += delta;
205 z += (y << 4 ^ y >> 5) + y ^ sum + key[sum>>11 & 3];
206 }
207
208 out[offset] = y;
209 out[offset+1] = z;
210 }
211
212 private final void decipher(final int[] in, final int[] out, final int offset)
213 {
214 int y = in[offset],
215 z = in[offset+1],
216 sum = SUM,
217 n = cycles;
218
219 /* sum = delta<<5, in general sum = delta * n */
220
221 while( n-- > 0 )
222 {
223 z -= (y << 4 ^ y >> 5) + y ^ sum + key[sum>>11 & 3];
224 sum -= delta;
225 y -= (z << 4 ^ z >> 5) + z ^ sum + key[sum&3];
226 }
227
228 out[offset] = y;
229 out[offset+1] = z;
230 }
231
232 private static int[] toInts(byte[] bytes)
233 {
234 int i, j;
235
236 i = bytes.length / 4;
237 int[] ints = new int[i];
238
239 for( i --, j = bytes.length - 1; i >= 0; i -- )
240 {
241 ints[i] = ( bytes[j--] & 0xFF ) << 24 |
242 ( bytes[j--] & 0xFF ) << 16 |
243 ( bytes[j--] & 0xFF ) << 8 |
244 ( bytes[j--] & 0xFF );
245 }
246
247 return ints;
248 }
249
250 private static byte[] toBytes(int[] ints)
251 {
252 int i, j;
253
254 i = ints.length * 4;
255 byte[] bytes = new byte[i];
256
257 for( i --, j = ints.length - 1; j >= 0; j -- )
258 {
259 bytes[i--] = (byte)( ( ints[j] >> 24 ) & 0xFF);
260 bytes[i--] = (byte)( ( ints[j] >> 16 ) & 0xFF);
261 bytes[i--] = (byte)( ( ints[j] >> 8 ) & 0xFF);
262 bytes[i--] = (byte)( ints[j] & 0xFF);
263 }
264
265 return bytes;
266 }
267
268 }