1 /**********************************************************\
2  *                                                        *
3  * xxtea.d                                                *
4  *                                                        *
5  * hprose bytes io library for D.                         *
6  *                                                        *
7  * LastModified: Feb 13, 2016                             *
8  * Author: Ma Bingyao <andot@hprose.com>                  *
9  *                                                        *
10 \**********************************************************/
11 
12 module xxtea;
13 
14 @safe:
15 
16 import std.base64;
17 import std.utf;
18 import std.traits;
19 
20 static class XXTEA {
21 private:
22     enum DELTA = 0x9e3779b9;
23     pure static uint mx(uint sum, uint y, uint z, ulong p, uint e, uint[] k) {
24         return (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
25     }
26     pure static uint[] encrypt(uint[] v, uint[] k) {
27         if (v.length < 2) return v;
28         ulong n = v.length - 1, p;
29         uint z = v[n], y, sum = 0u, e;
30         int q = 6 + 52 / (n + 1);
31         while (0 < q--) {
32             sum += DELTA;
33             e = sum >> 2 & 3;
34             for (p = 0; p < n; p++) {
35                 y = v[p + 1];
36                 z = v[p] += mx(sum, y, z, p, e, k);
37             }
38             y = v[0];
39             z = v[n] += mx(sum, y, z, p, e, k);
40         }
41         return v;
42     }
43     pure static uint[] decrypt(uint[] v, uint[] k) {
44         if (v.length < 2) return v;
45         ulong n = v.length - 1, p;
46         uint z, y = v[0], sum, e;
47         int q = 6 + 52 / (n + 1);
48         sum = q * DELTA;
49         while (sum != 0) {
50             e = sum >> 2 & 3;
51             for (p = n; p > 0; p--) {
52                 z = v[p - 1];
53                 y = v[p] -= mx(sum, y, z, p, e, k);
54             }
55             z = v[n];
56             y = v[0] -= mx(sum, y, z, p, e, k);
57             sum -= DELTA;
58         }
59         return v;
60     }
61     pure static const(ubyte[]) fixkey(in ubyte[] key) {
62         if (key.length == 16) return key;
63         if (key.length > 16) {
64             return key[0..16];
65         }
66         else {
67             ubyte[] fixedkey = key.dup();
68             fixedkey.length = 16;
69             return fixedkey;
70         }
71     }
72     pure static uint[] touints(in ubyte[] data, bool includeLength) {
73         ulong length = data.length;
74         ulong n = (((length & 3) == 0) ? (length >> 2) : ((length >> 2) + 1));
75         uint[] result;
76         if (includeLength) {
77             result = new uint[n + 1];
78             result[n] = cast(uint)length;
79         }
80         else {
81             result = new uint[n];
82         }
83         for (ulong i = 0; i < length; ++i) {
84             result[i >> 2] |= cast(uint)data[i] << ((i & 3) << 3);
85         }
86         return result;
87     }
88     pure static ubyte[] tobytes(in uint[] data, bool includeLength) {
89         ulong n = data.length << 2;
90         if (includeLength) {
91             ulong m = data[data.length - 1];
92             n -= 4;
93             if ((m < n - 3) || (m > n)) {
94                 return null;
95             }
96             n = m;
97         }
98         ubyte[] result = new ubyte[n];
99         for (ulong i = 0; i < n; ++i) {
100             result[i] = cast(ubyte)(data[i >> 2] >> ((i & 3) << 3));
101         }
102         return result;
103     }
104 public:
105     pure static ubyte[] encrypt(in ubyte[] data, in ubyte[] key) {
106         if (data.length == 0) return null;
107         return tobytes(encrypt(touints(data, true), touints(fixkey(key), false)), false);
108     }
109     pure static ubyte[] decrypt(in ubyte[] data, in ubyte[] key) {
110         if (data.length == 0) return null;
111         return tobytes(decrypt(touints(data, false), touints(fixkey(key), false)), true);
112     }
113     pure static ubyte[] encrypt(T, U)(in T data, in U key)
114         if ((isSomeString!T || is(T == byte[]) || is(T == ubyte[])) &&
115             (isSomeString!U || is(U == byte[]) || is(U == ubyte[]))) {
116         static if (isSomeString!T && isSomeString!U) {
117             return encrypt(cast(const(ubyte[]))toUTF8(data), cast(const(ubyte[]))toUTF8(key));
118         }
119         else static if (isSomeString!T && !isSomeString!U) {
120             return encrypt(cast(const(ubyte[]))toUTF8(data), cast(const(ubyte[]))key);
121         }
122         else static if (!isSomeString!T && isSomeString!U) {
123             return encrypt(cast(const(ubyte[]))data, cast(const(ubyte[]))toUTF8(key));
124         }
125         else {
126             return encrypt(cast(const(ubyte[]))data, cast(const(ubyte[]))key);
127         }
128     }
129     pure static ubyte[] decrypt(T)(in ubyte[] data, in T key)
130     if (isSomeString!T || is(T == byte[])) {
131         static if (isSomeString!T) {
132             return decrypt(data, cast(const(ubyte[]))toUTF8(key));
133         }
134         else {
135             return decrypt(data, cast(const(ubyte[]))key);
136         }
137     }
138     pure static string encryptToBase64(T, U)(in T data, in U key) 
139         if ((isSomeString!T || is(T == byte[]) || is(T == ubyte[])) &&
140         (isSomeString!U || is(U == byte[]) || is(U == ubyte[]))) {
141         return Base64.encode(encrypt(data, key));
142     }
143     pure static ubyte[] decryptFromBase64(T)(in string data, in T key)
144     if (isSomeString!T || is(T == byte[]) || is(T == ubyte[])) {
145         return decrypt(Base64.decode(data), key);
146     }
147 }
148 
149 unittest {
150     enum text = "Hello World! 你好,中国!";
151     enum key = "1234567890";
152     enum encrypt_data = XXTEA.encryptToBase64(text, key);
153     enum decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
154     static assert(text == decrypt_data);
155 }
156 
157 unittest {
158     enum text = "Hello World! 你好,中国!";
159     enum key = "1234567890";
160     enum encrypt_data = XXTEA.encrypt(text, key);
161     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
162     static assert(text == decrypt_data);
163 }
164 
165 
166 unittest {
167     import std.conv;
168     enum text = to!(wchar[])("Hello World! 你好,中国!");
169     enum key = "1234567890";
170     enum encrypt_data = XXTEA.encryptToBase64(text, key);
171     enum decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
172     static assert(toUTF8(text) == decrypt_data);
173 }
174 
175 unittest {
176     import std.conv;
177     enum text = to!(wchar[])("Hello World! 你好,中国!");
178     enum key = "1234567890";
179     enum encrypt_data = XXTEA.encrypt(text, key);
180     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
181     static assert(toUTF8(text) == decrypt_data);
182 }
183 
184 unittest {
185     enum text = cast(wstring)"Hello World! 你好,中国!";
186     enum key = "1234567890";
187     enum encrypt_data = XXTEA.encryptToBase64(text, key);
188     enum decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
189     static assert(toUTF8(text) == decrypt_data);
190 }
191 
192 unittest {
193     enum text = cast(wstring)"Hello World! 你好,中国!";
194     enum key = "1234567890";
195     enum encrypt_data = XXTEA.encrypt(text, key);
196     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
197     static assert(toUTF8(text) == decrypt_data);
198 }
199 
200 unittest {
201     import std.conv;
202     enum text = to!(dchar[])("Hello World! 你好,中国!");
203     enum key = "1234567890";
204     enum encrypt_data = XXTEA.encryptToBase64(text, key);
205     enum decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
206     static assert(toUTF8(text) == decrypt_data);
207 }
208 
209 unittest {
210     import std.conv;
211     enum text = to!(dchar[])("Hello World! 你好,中国!");
212     enum key = "1234567890";
213     enum encrypt_data = XXTEA.encrypt(text, key);
214     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
215     static assert(toUTF8(text) == decrypt_data);
216 }
217 
218 unittest {
219     enum text = cast(dstring)"Hello World! 你好,中国!";
220     enum key = "1234567890";
221     enum encrypt_data = XXTEA.encryptToBase64(text, key);
222     enum decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
223     static assert(toUTF8(text) == decrypt_data);
224 }
225 
226 unittest {
227     enum text = cast(dstring)"Hello World! 你好,中国!";
228     enum key = "1234567890";
229     enum encrypt_data = XXTEA.encrypt(text, key);
230     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
231     static assert(toUTF8(text) == decrypt_data);
232 }
233 
234 unittest {
235     auto text = "Hello World! 你好,中国!";
236     auto key = "1234567890";
237     auto encrypt_data = XXTEA.encryptToBase64(text, key);
238     auto decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
239     assert(text == decrypt_data);
240 }
241 
242 unittest {
243     auto text = "Hello World! 你好,中国!";
244     auto key = "1234567890";
245     auto encrypt_data = XXTEA.encrypt(text, key);
246     auto decrypt_data = XXTEA.decrypt(encrypt_data, key);
247     assert(text == decrypt_data);
248 }
249 
250 unittest {
251     import std.conv;
252     auto text = to!(wchar[])("Hello World! 你好,中国!");
253     auto key = "1234567890";
254     auto encrypt_data = XXTEA.encryptToBase64(text, key);
255     auto decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
256     assert(toUTF8(text) == decrypt_data);
257 }
258 
259 unittest {
260     import std.conv;
261     auto text = to!(wchar[])("Hello World! 你好,中国!");
262     auto key = "1234567890";
263     auto encrypt_data = XXTEA.encrypt(text, key);
264     auto decrypt_data = XXTEA.decrypt(encrypt_data, key);
265     assert(toUTF8(text) == decrypt_data);
266 }
267 
268 unittest {
269     auto text = cast(wstring)"Hello World! 你好,中国!";
270     auto key = "1234567890";
271     auto encrypt_data = XXTEA.encryptToBase64(text, key);
272     auto decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
273     assert(toUTF8(text) == decrypt_data);
274 }
275 
276 unittest {
277     auto text = cast(wstring)"Hello World! 你好,中国!";
278     auto key = "1234567890";
279     auto encrypt_data = XXTEA.encrypt(text, key);
280     auto decrypt_data = XXTEA.decrypt(encrypt_data, key);
281     assert(toUTF8(text) == decrypt_data);
282 }
283 
284 unittest {
285     import std.conv;
286     auto text = to!(dchar[])("Hello World! 你好,中国!");
287     auto key = "1234567890";
288     auto encrypt_data = XXTEA.encryptToBase64(text, key);
289     auto decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
290     assert(toUTF8(text) == decrypt_data);
291 }
292 
293 unittest {
294     import std.conv;
295     enum text = to!(dchar[])("Hello World! 你好,中国!");
296     enum key = "1234567890";
297     enum encrypt_data = XXTEA.encrypt(text, key);
298     enum decrypt_data = XXTEA.decrypt(encrypt_data, key);
299     static assert(toUTF8(text) == decrypt_data);
300 }
301 
302 unittest {
303     auto text = cast(dstring)"Hello World! 你好,中国!";
304     auto key = "1234567890";
305     auto encrypt_data = XXTEA.encryptToBase64(text, key);
306     auto decrypt_data = XXTEA.decryptFromBase64(encrypt_data, key);
307     assert(toUTF8(text) == decrypt_data);
308 }
309 
310 unittest {
311     auto text = cast(dstring)"Hello World! 你好,中国!";
312     auto key = "1234567890";
313     auto encrypt_data = XXTEA.encrypt(text, key);
314     auto decrypt_data = XXTEA.decrypt(encrypt_data, key);
315     assert(toUTF8(text) == decrypt_data);
316 }