1 module crypto.tea;
2 
3 import std.bitmanip;
4 import std.exception;
5 
6 package struct TEA
7 {
8     private enum int DELTA = cast(int) 0x9E3779B9;
9     private int[4] m_key;
10     private int    m_rounds;
11 
12     public this(int[4] key)
13     {
14         m_key    = key;
15         m_rounds = 32;
16     }
17 
18     /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
19     public alias Crypt!(EncryptBlock) Encrypt;
20     /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
21     public alias Crypt!(DecryptBlock) Decrypt;
22 
23     private const void Crypt(alias T)(ubyte[] _ubytes, size_t _offset = 0, long _count = -1)
24     {
25         if (_count == -1)
26         {
27             _count = cast(long)(_ubytes.length - _offset);
28         }
29 
30         enforce(_count % 8 == 0);
31 
32         for (size_t i = _offset; i < (_offset + _count); i += 8)
33         {
34             T(_ubytes, i);
35         }
36     }
37 
38     /// Encrypt given block of 8 ubytes
39     private const void EncryptBlock(ubyte[] _ubytes, size_t _offset)
40     {
41         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
42         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
43 
44         int sum = 0;
45 
46         foreach (i; 0 .. m_rounds)
47         {
48             sum += DELTA;
49             v0 += ((v1 << 4) + m_key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + m_key[1]);
50             v1 += ((v0 << 4) + m_key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + m_key[3]);
51         }
52 
53         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
54         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
55     }
56 
57     /// Decrypt given block of 8 ubytes
58     private const void DecryptBlock(ubyte[] _ubytes, size_t _offset)
59     {
60         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
61         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
62 
63         auto sum = cast(int)(cast(uint) DELTA * cast(uint) m_rounds);  //0xC6EF3720
64 
65         foreach (i; 0 .. m_rounds)
66         {
67             v1 -= ((v0 << 4) + m_key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + m_key[3]);
68             v0 -= ((v1 << 4) + m_key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + m_key[1]);
69             sum -= DELTA;
70         }
71             
72         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
73         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
74     }
75 }
76 
77 class Tea
78 {
79 	public static ubyte[] encrypt(in ubyte[] input, in char[] key)
80 	{
81 		ubyte[] buf = cast(ubyte[])key;
82 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
83 
84 		return encrypt(input, bkey);
85 	}
86 	
87     public static ubyte[] encrypt(in ubyte[] input, int[4] key)
88     {
89         ubyte[] data = input.dup;
90         int orgi_len = cast(int)data.length;
91 
92 		while ((data.length + 4) % 8 != 0)
93         {
94             data ~= 0;
95         }
96 
97         ubyte[] len_buf = new ubyte[4];
98         len_buf.write!int(orgi_len, 0);
99         data ~= len_buf;
100 
101         TEA tea = TEA(key);
102         tea.Encrypt(data);
103 
104         return data;
105     }
106 
107 	public static ubyte[] decrypt(in ubyte[] input, in char[] key)
108 	{
109 		ubyte[] buf = cast(ubyte[])key;
110 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
111 
112 		return decrypt(input, bkey);
113 	}
114 
115     public static ubyte[] decrypt(in ubyte[] input, int[4] key)
116     {
117         auto data = input.dup;
118         TEA tea = TEA(key);
119         tea.Decrypt(data);
120 
121         int orgi_len;
122         orgi_len = data.peek!int(data.length - 4);
123 
124         return data[0 .. orgi_len];
125     }
126 
127     unittest
128     {
129         import std.stdio;
130         import crypto.tea;
131 
132         ubyte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
133         int[4] key = [1, 2, 3, 4];
134 
135         ubyte[] buf = Tea.encrypt(data, key);
136         writeln(buf);
137         buf = Tea.decrypt(buf, key);
138         writeln(buf);
139     }
140 }
141 
142 package struct XTEA
143 {
144     private enum int DELTA = cast(int) 0x9E3779B9;
145     private int[4] m_key;
146     private int m_rounds;
147 
148     public this(int[4] key, int rounds)
149     {
150         m_key    = key;
151         m_rounds = rounds;
152     }
153 
154     /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
155     public alias Crypt!(EncryptBlock) Encrypt;
156     /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
157     public alias Crypt!(DecryptBlock) Decrypt;
158 
159     private const void Crypt(alias T)(ubyte[] _ubytes, size_t _offset = 0, long _count = -1)
160     {
161         if (_count == -1)
162         {
163             _count = cast(long)(_ubytes.length - _offset);
164         }
165 
166         enforce(_count % 8 == 0);
167 
168         for (size_t i = _offset; i < (_offset + _count); i += 8)
169         {
170             T(_ubytes, i);
171         }
172     }
173 
174     /// Encrypt given block of 8 ubytes
175     private const void EncryptBlock(ubyte[] _ubytes, size_t _offset)
176     {
177         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
178         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
179 
180         int sum = 0;
181 
182         foreach (i; 0 .. m_rounds)
183         {
184             v0 += ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
185             sum += DELTA;
186             v1 += ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]);
187         }
188 
189         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
190         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
191     }
192 
193     /// Decrypt given block of 8 ubytes
194     private const void DecryptBlock(ubyte[] _ubytes, size_t _offset)
195     {
196         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
197         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
198 
199         auto sum = cast(int)(cast(uint) DELTA * cast(uint) m_rounds);
200 
201         foreach (i; 0 .. m_rounds)
202         {
203             v1 -= ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]);
204             sum -= DELTA;
205             v0 -= ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
206         }
207 
208         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
209         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
210     }
211 }
212 
213 class Xtea
214 {
215 	public static ubyte[] encrypt(in ubyte[] input, in char[] key, int rounds = 64)
216 	{
217 		ubyte[] buf = cast(ubyte[])key;
218 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
219 
220 		return encrypt(input, bkey, rounds);
221 	}
222 	
223     public static ubyte[] encrypt(in ubyte[] input, int[4] key, int rounds = 64)
224     {
225         ubyte[] data = input.dup;
226         int orgi_len = cast(int)data.length;
227 
228 		while ((data.length + 4) % 8 != 0)
229         {
230             data ~= 0;
231         }
232 
233         ubyte[] len_buf = new ubyte[4];
234         len_buf.write!int(orgi_len, 0);
235         data ~= len_buf;
236 
237         XTEA xtea = XTEA(key, rounds);
238         xtea.Encrypt(data);
239 
240         return data;
241     }
242 
243 	public static ubyte[] decrypt(in ubyte[] input, in char[] key, int rounds = 64)
244 	{
245 		ubyte[] buf = cast(ubyte[])key;
246 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
247 
248 		return decrypt(input, bkey, rounds);
249 	}
250 
251     public static ubyte[] decrypt(in ubyte[] input, int[4] key, int rounds = 64)
252     {
253         auto data = input.dup;
254         XTEA xtea = XTEA(key, rounds);
255         xtea.Decrypt(data);
256 
257         int orgi_len;
258         orgi_len = data.peek!int(data.length - 4);
259 
260         return data[0 .. orgi_len];
261     }
262 
263     unittest
264     {
265         import std.stdio;
266         import crypto.tea;
267 
268         ubyte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
269         int[4] key   = [1, 2, 3, 4];
270         int rounds   = 64;
271 
272         ubyte[] buf = Xtea.encrypt(data, key, rounds);
273         writeln(buf);
274         buf = Xtea.decrypt(buf, key, rounds);
275         writeln(buf);
276     }
277 }