1 module crypto.tea.xtea;
2 
3 import std.bitmanip;
4 import std.exception;
5 
6 package struct XTEA
7 {
8     /// XTEA delta constant
9     private enum int DELTA = cast(int) 0x9E3779B9;
10 
11     /// Key - 4 integer
12     private int[4] m_key;
13 
14     /// Round to go - 64 are commonly used
15     private int m_rounds;
16 
17     /// c'tor
18     public this(int[4] _key, int _rounds)
19     {
20         m_key = _key;
21         m_rounds = _rounds;
22     }
23 
24     /// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
25     public alias Crypt!(EncryptBlock) Encrypt;
26     /// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
27     public alias Crypt!(DecryptBlock) Decrypt;
28 
29     ///
30     private const void Crypt(alias T)(ubyte[] _ubytes, size_t _offset = 0, long _count = -1)
31     {
32         if (_count == -1)
33             _count = cast(long)(_ubytes.length - _offset);
34 
35         enforce(_count % 8 == 0);
36 
37         for (size_t i = _offset; i < (_offset + _count); i += 8)
38             T(_ubytes, i);
39     }
40 
41     /// Encrypt given block of 8 ubytes
42     private const void EncryptBlock(ubyte[] _ubytes, size_t _offset)
43     {
44         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
45         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
46 
47         int sum = 0;
48 
49         foreach (i; 0 .. m_rounds)
50         {
51             v0 += ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
52             sum += DELTA;
53             v1 += ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (
54                     sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]);
55         }
56 
57         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
58         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
59     }
60 
61     /// Decrypt given block of 8 ubytes
62     private const void DecryptBlock(ubyte[] _ubytes, size_t _offset)
63     {
64         auto v0 = _ubytes.peek!(int, Endian.littleEndian)(_offset);
65         auto v1 = _ubytes.peek!(int, Endian.littleEndian)(_offset + 4);
66 
67         auto sum = cast(int)(cast(uint) DELTA * cast(uint) m_rounds);
68 
69         foreach (i; 0 .. m_rounds)
70         {
71             v1 -= ((v0 << 4 ^ cast(int)(cast(uint) v0 >> 5)) + v0) ^ (
72                     sum + m_key[cast(int)(cast(uint) sum >> 11) & 3]);
73             sum -= DELTA;
74             v0 -= ((v1 << 4 ^ cast(int)(cast(uint) v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
75         }
76 
77         _ubytes.write!(int, Endian.littleEndian)(v0, _offset);
78         _ubytes.write!(int, Endian.littleEndian)(v1, _offset + 4);
79     }
80 }
81 
82 class Xtea
83 {
84 	public static ubyte[] encrypt(ubyte[] input, string key, int rounds = 64)
85 	{
86 		ubyte[] buf = cast(ubyte[])key;
87 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
88 
89 		return encrypt(input, bkey, rounds);
90 	}
91 	
92     public static ubyte[] encrypt(ubyte[] input, int[4] key, int rounds = 64)
93     {
94         ubyte[] data = input.dup;
95         int orgi_len = cast(int)data.length;
96 		while ((data.length + 4) % 8 != 0)
97             data ~= 0;
98             
99         ubyte[] len_buf = new ubyte[4];
100         len_buf.write!int(orgi_len, 0);
101         data ~= len_buf;
102 
103         XTEA xeta = XTEA(key, rounds);
104         xeta.Encrypt(data);
105         return data;
106     }
107 
108 	public static ubyte[] decrypt(ubyte[] input, string key, int rounds = 64)
109 	{
110 		ubyte[] buf = cast(ubyte[])key;
111 		int[4] bkey = [buf[0], buf[1], buf[2], buf[3]];
112 
113 		return decrypt(input, bkey, rounds);
114 	}
115 
116     public static ubyte[] decrypt(ubyte[] input, int[4] key, int rounds = 64)
117     {
118         auto data = input.dup;
119         XTEA xeta = XTEA(key, rounds);
120         xeta.Decrypt(data);
121 
122         int orgi_len;
123         orgi_len = data.peek!int(data.length - 4);
124         return data[0 .. orgi_len];
125     }
126 
127     unittest
128     {
129         import crypto.tea.xtea;
130 
131         ubyte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
132         int[4] key = [1, 2, 3, 4];
133         int rounds = 64;
134 
135         ubyte[] buf = Xtea.encrypt(data, key, rounds);
136         writeln(buf);
137         buf = Xtea.decrypt(buf, key, rounds);
138         writeln(buf);
139     }
140 }