1 module xtea.XteaCrypto;
2 @safe:
3 nothrow:
4 pure:
5 
6 ubyte[] encrypt(XTEA crypto, const(ubyte)[] m)
7 {
8 	auto ans = m.dup;
9 	crypto.Encrypt(ans);
10 	return ans;
11 }
12 @nogc unittest
13 {
14 
15 	enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823];
16 	enum encrypted = XTEA([1,2,3,4], 64).encrypt(sourceData);
17 	import std.algorithm:equal;
18 	static assert(encrypted.equal([49, 144, 90, 128, 35, 56, 194, 0]));
19 }
20 
21 ubyte[] decrypt(XTEA crypto, const(ubyte)[] m) {
22 	auto ans = m.dup;
23 	crypto.Decrypt(ans);
24 	return ans;
25 }
26 unittest
27 {
28 	enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823];
29 	enum encrypted = cast(ubyte[])[49, 144, 90, 128, 35, 56, 194, 0];
30 	enum dencrypted = XTEA([1,2,3,4], 64).decrypt(encrypted);
31 	import std.algorithm:equal;
32 	static assert(dencrypted.equal(sourceData));
33 }
34 // Coverage %
35 unittest {
36 	enum sourceData = cast(ubyte[])[6, 2, 87, 66, 77, 289, 623, 39823];
37 	enum crypto = XTEA([1,2,3,4], 64);
38 	auto runtime = crypto.decrypt(crypto.encrypt(sourceData));
39 	import std.algorithm:equal;
40 	assert(runtime.equal(sourceData));
41 }
42 
43 /++
44  +	XTEA helper type
45  +	see: http://en.wikipedia.org/wiki/XTEA
46 +/
47 public struct XTEA
48 {
49 @safe:
50 nothrow:
51 pure:
52 @nogc:
53 
54 	/// XTEA delta constant
55 	private enum int DELTA = cast(int)0x9E3779B9;
56 
57 	/// Key - 4 integer
58 	private int[4] m_key;
59 
60 	/// Round to go - 64 are commonly used
61 	private int m_rounds;
62 
63 	/// c'tor
64 	public this(int[4] _key, int _rounds)
65 	{
66 		m_key = _key;
67 		m_rounds = _rounds;
68 	}
69 
70 	~this() @trusted
71 	{
72         version (LDC) if (!__ctfe)
73         {
74             import ldc.intrinsics : llvm_memset;
75             // llvm_memset with the last parameter set to "true" is guaranteed
76             // not to be elided by the compiler even if the compiler determines
77             // the new values are never read.
78             static if (is(typeof(llvm_memset(array.ptr, 0, array.length * T.sizeof, true)))) // LLVM 7+
79                 llvm_memset(&m_key, 0, m_key.sizeof, true);
80             else // Pre-LLVM 7
81                 llvm_memset(&m_key, 0, m_key.sizeof, m_key.alignof, true);
82             return;
83         }
84         m_key[] = 0;
85 	}
86 
87 	/// Encrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
88 	public alias Crypt!(EncryptBlock) Encrypt;
89 	/// Decrypt given ubyte array (length to be crypted must be 8 ubyte aligned)
90 	public alias Crypt!(DecryptBlock) Decrypt;
91 
92 	///
93 	private const void Crypt(alias T)(ubyte[] _ubytes, size_t _offset=0, long _count=-1)
94 	{
95 		if(_count == -1)
96 			_count = cast(long)(_ubytes.length - _offset);
97 
98 		assert(_count % 8 == 0);
99 
100 		for (size_t i = _offset; i < (_offset+_count); i += 8)
101 			T(_ubytes, i);
102 	}
103 
104 	/// Encrypt given block of 8 ubytes
105 	private const void EncryptBlock(ubyte[] _ubytes, size_t _offset)
106 	{
107 		auto v0 = ReadInt(_ubytes, _offset);
108 		auto v1 = ReadInt(_ubytes, _offset + 4);
109 
110 		int sum = 0;
111 
112 		foreach (i; 0..m_rounds)
113 		{
114 			v0 += ((v1 << 4 ^ cast(int)(cast(uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
115 			sum += DELTA;
116 			v1 += ((v0 << 4 ^ cast(int)(cast(uint)v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint)sum >> 11) & 3]);
117 		}
118 
119 		StoreInt(v0, _ubytes, _offset);
120 		StoreInt(v1, _ubytes, _offset + 4);
121 	}
122 
123 	/// Decrypt given block of 8 ubytes
124 	private const void DecryptBlock(ubyte[] _ubytes, size_t _offset)
125 	{
126 		auto v0 = ReadInt(_ubytes, _offset);
127 		auto v1 = ReadInt(_ubytes, _offset + 4);
128 
129 		auto sum = cast(int)(cast(uint)DELTA * cast(uint)m_rounds);
130 
131 		foreach (i; 0..m_rounds)
132 		{
133 			v1 -= ((v0 << 4 ^ cast(int)(cast(uint)v0 >> 5)) + v0) ^ (sum + m_key[cast(int)(cast(uint)sum >> 11) & 3]);
134 			sum -= DELTA;
135 			v0 -= ((v1 << 4 ^ cast(int)(cast(uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
136 		}
137 
138 		StoreInt(v0, _ubytes, _offset);
139 		StoreInt(v1, _ubytes, _offset + 4);
140 	}
141 
142 	/// Read 32 bit int from buffer
143 	private static int ReadInt(ubyte[] _ubytes, size_t _offset)
144 	{
145 		return (((_ubytes[_offset++] & 0xff) << 0)
146 				| ((_ubytes[_offset++] & 0xff) << 8)
147 				| ((_ubytes[_offset++] & 0xff) << 16)
148 				| ((_ubytes[_offset] & 0xff) << 24));
149 	}
150 
151 	/// Write 32 bit int from buffer
152 	private static void StoreInt(int _value, ubyte[] _ubytes, size_t _offset)
153 	{
154 		auto unsignedValue = cast(uint)_value;
155 		_ubytes[_offset++] = cast(ubyte)(unsignedValue >> 0);
156 		_ubytes[_offset++] = cast(ubyte)(unsignedValue >> 8);
157 		_ubytes[_offset++] = cast(ubyte)(unsignedValue >> 16);
158 		_ubytes[_offset] = cast(ubyte)(unsignedValue >> 24);
159 	}
160 }
161 
162 @nogc unittest
163 {
164     ubyte[16] sourceData = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
165     ubyte[16] data       = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
166 
167     auto crypto = XTEA([1,2,3,4], 64);
168 
169     crypto.Encrypt(data);
170     crypto.Decrypt(data);
171     assert(sourceData == data);
172 }